如何将 C 风格的宏与 C++11 风格的构造函数调用一起使用?

How can I use C-style macros with C++11-style constructor calls?

我发现使用 C 风格的宏和使用 C++11 中引入的新的统一列表初始化形式之间似乎存在不兼容性,但这种事情绝对不可能发生,这似乎令人难以置信写,所以我想我错过了一些东西。

问题是:当预处理器查找宏参数时,花括号似乎被忽略了。像 MACR(Range{2,4}) 这样的调用被误解为有两个参数,Range{24。在下面的代码中,一切都很好(好吧,风格很差,但它有效)直到标记行:

#include <iostream>
using namespace std;

struct Range { int st, fn; };
ostream& operator << (ostream& out, const Range& r)
{ return out << "(" << r.st << "," << r.fn << ")"; }

#define COUT(X) (cout << (X) << endl)

int main()
{
  COUT(3);
  Range r {3,5};
  COUT(r);

  COUT(Range{3,5});  //this line won't compile
}

它给出了以下错误信息:

badmacro.cpp:16:18: error: macro "COUT" passed 2 arguments, but takes just 1
   COUT(Range{3,5});
                  ^
compilation terminated due to -Wfatal-errors.

尤其是在使用较旧的库时,有时不可避免地要使用宏调用;在这些情况下,我们当然不应该放弃新语法吗?有官方的解决方法吗?

预处理器宏只是编译前花哨的文本替换。调用宏时,预处理器对参数列表的解析很少。有一些逻辑可以区分嵌套括号内部和外部的逗号,因此它知道哪些逗号属于宏本身的参数列表,哪些逗号属于嵌套函数调用的参数列表。例如:

macro(param1, param2, func(param1, param2) )

参数解释为

param1
param2
func(param1, param2)

而不是

param1
param2
func(param1
param2)

在你的情况下,你的逗号不在嵌套括号内,因此预处理器最终将参数列表 Range{3,5} 拆分为两个参数值

Range{3
5}

因此出现错误,因为您的宏只接受一个参数。预处理器没有任何上下文信息知道 Range{3,5} 应该被视为一个参数值。它只看到逗号并在其上拆分。

所以,要解决您的问题,请尝试添加一对额外的括号:

COUT((Range{3,5}));

然后预处理器应该解释一个参数值:

(Range{3,5})

这将创建以下语句供编译器使用:

(cout << ((Range{3,5})) << endl);

如果您需要将表达式传递给现有的宏,并且表达式包含未屏蔽的逗号,只需将整个表达式括在括号中即可。

COUT((Range{3,5}));

丑?是的,但这就是您使用宏时发生的情况。不要那样做。

如果它不是表达式并且不能带额外的括号,那么您根本无法使用该宏。

如果您正在编写一个您不应该编写的宏,有时您可以编写一个可变参数宏(如果您的编译器支持):

#define COUT(...) cout << (__VA_ARGS__) << endl;