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

如何将C风格的宏与C++11风格的构造函数调用一起使用?,c++11,c-preprocessor,C++11,C Preprocessor,我发现使用C风格的宏和使用C++11中引入的新的统一列表初始化表单之间似乎存在不兼容,但这类东西绝对不可能编写,这似乎令人难以置信,因此我认为我遗漏了一些东西 问题是:当预处理器查找宏参数时,花括号似乎被忽略。像MACRRange{2,4}这样的调用被错误地解释为有两个参数,范围为{2和4。在以下代码中,这一切都很好,样式很差,但在标记行之前都有效: #include <iostream> using namespace std; struct Range { int st, fn

我发现使用C风格的宏和使用C++11中引入的新的统一列表初始化表单之间似乎存在不兼容,但这类东西绝对不可能编写,这似乎令人难以置信,因此我认为我遗漏了一些东西

问题是:当预处理器查找宏参数时,花括号似乎被忽略。像MACRRange{2,4}这样的调用被错误地解释为有两个参数,范围为{2和4。在以下代码中,这一切都很好,样式很差,但在标记行之前都有效:

#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
}

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

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

macro(param1, param2, func(param1, param2) )
这些参数被解释为

param1
param2
func(param1, param2)
而不是

param1
param2
func(param1
param2)
在本例中,逗号不在嵌套括号内,因此预处理器最终将参数列表范围{3,5}拆分为两个参数值

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

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

COUT((Range{3,5}));
然后,预处理器应解释一个参数值:

(Range{3,5})
这将创建以下语句供编译器使用:

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

预处理器宏在编译之前只是一种奇特的文本替换。在调用宏时,预处理器对参数列表的分析很少。嵌套括号内的逗号与括号外的逗号之间有一定的区别,因此它知道哪些逗号属于宏本身的参数列表,而不是逗号用于嵌套函数调用的参数列表。例如:

macro(param1, param2, func(param1, param2) )
这些参数被解释为

param1
param2
func(param1, param2)
而不是

param1
param2
func(param1
param2)
在本例中,逗号不在嵌套括号内,因此预处理器最终将参数列表范围{3,5}拆分为两个参数值

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

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

COUT((Range{3,5}));
然后,预处理器应解释一个参数值:

(Range{3,5})
这将创建以下语句供编译器使用:

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

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

COUT((Range{3,5}));
难看?是的,但当你使用宏时会发生这种情况。不要这样做


如果它不是一个表达式,并且不能使用额外的括号,那么就不能使用该宏

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

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

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

COUT((Range{3,5}));
难看?是的,但当你使用宏时会发生这种情况。不要这样做


如果它不是一个表达式,并且不能使用额外的括号,那么就不能使用该宏

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

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

@Remylabau一个C++11编译器应该支持这一点。在C++11之前的C99中引入了可变宏。还不是所有人都在使用C++11编译器,也不是所有C++11之前的编译器都支持可变宏。如果它不是一个表达式,不能使用额外的括号,那么它仍然可以工作。对于这个特定的实例,定义ID。。。然后是COUTIDRange{3,5}@Remylabou这种初始化方式在C++11中是新的,并且已经需要C++11编译器。如果已经需要C++11编译器,您也可以使用它。@Remylabou C++11编译器应该支持它。在C++11之前,C99中引入了可变宏。还不是所有人都使用C++11编译器,也不是所有C++11编译之前的编译器rs支持一些变量宏。如果它不是一个表达式并且不能使用额外的括号,那么它仍然可以工作。对于这个特殊的例子,定义ID…。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。VA!ARGS,然后是COUTIDRange{3,5}。@RemyLebeau这种初始化方式在C++11和alre中是新的 ady需要一个C++11编译器。如果已经需要C++11编译器,您也可以使用它。