编译器之间的宏扩展顺序混淆

时间:2017-05-30 13:16:51

标签: c++ visual-studio-2015 macros clang

这段代码在Visual Studio 2015中编译,但不在Clang中编译:

#define COMMA ,
#define MC(a) a
#define MA(a,b,c) MC(a b c)
map <MA(int,COMMA,int)> FF;

看来Clang在将 COMMA 宏提交给MC()宏之前会扩展它。根据C ++标准,“谁是对的”?另外,我怎样才能让Clang表现得像Visual Studio?

编辑:简化了示例,并更改了一些宏名称。

1 个答案:

答案 0 :(得分:0)

Clang符合标准; Visual Studio没有。我认为让Clang不符合标准会有很多麻烦,所以我不会试图回答“我如何让Clang像Visual Studio一样行事?”。也许那不是你想知道的。

当编译器识别出类似函数宏的调用(即带参数的宏)时,它使用C ++标准的§16.3[cpp.replace]中详细说明的过程扩展宏。在下文中,我通过不考虑###运算符简化了过程,因为它们没有出现在您的示例中,并且完整过程更复杂。

我们将检查调用MC(int, COMMA, int)。这是在编译器看到令牌MC(之后发生的事情,它表示调用宏。

  1. 编译器识别参数是什么,包括查找右括号。有三个参数,对应于参数的数量,所以没关系。参数尚未扩展,因此编译器只能在源文件中看到标点符号。它将参数标识为intCOMMAint

  2. 每个参数(除了相应参数参与标记连接或字符串化的参数 - 但正如我所说,我不会在这里进入那个场景)然后完全展开。这在它们被替换到宏体之前发生,因此宏的参数的名称不会从宏中泄漏出来。所以现在这三个论点是int,int

  3. 制作宏体的副本,其中每个参数都用相应的(完全展开的)参数代替。宏观体(“替换列表”,标准符号)是MC(A B C);在替换参数后,变为MC(A , C)

  4. 将步骤3中创建的标记序列插入到输入中以代替宏调用,并继续预处理。

  5. 此时,编译器将看到函数式宏MC(A, B)的调用,并将如上所述继续。但是,这次第一步失败,因为识别出两个参数但宏MC只有一个参数。