如何将 C 风格的宏与 C++11 风格的构造函数调用一起使用?
How can I use C-style macros with C++11-style constructor calls?
我发现使用 C 风格的宏和使用 C++11 中引入的新的统一列表初始化形式之间似乎存在不兼容性,但这种事情绝对不可能发生,这似乎令人难以置信写,所以我想我错过了一些东西。
问题是:当预处理器查找宏参数时,花括号似乎被忽略了。像 MACR(Range{2,4})
这样的调用被误解为有两个参数,Range{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
}
它给出了以下错误信息:
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;
我发现使用 C 风格的宏和使用 C++11 中引入的新的统一列表初始化形式之间似乎存在不兼容性,但这种事情绝对不可能发生,这似乎令人难以置信写,所以我想我错过了一些东西。
问题是:当预处理器查找宏参数时,花括号似乎被忽略了。像 MACR(Range{2,4})
这样的调用被误解为有两个参数,Range{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
}
它给出了以下错误信息:
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;