这段代码可以在 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 一样?
编辑:简化了示例,并更改了一些宏名称。
最佳答案
Clang 符合标准; Visual Studio 没有。我认为让 Clang 不符合标准会很麻烦,所以我不会尝试回答“如何让 Clang 像 Visual Studio 一样工作?”。也许这并不是您想知道的。
当编译器识别出类函数宏(即带参数的宏)的调用时,它会使用 §16.3 [cpp.replace]
中详细解释的过程扩展宏C++标准。在下文中,我通过不考虑 #
和 ##
运算符来简化过程,因为它们没有出现在您的示例中并且整个过程更加复杂。
我们将检查调用 MC(int, COMMA, int)
。以下是编译器看到标记 MC
和 (
后发生的情况,它们指示宏的调用。
编译器识别参数是什么,这涉及找到右括号。参数是三个,对应参数个数,这样就OK了。参数尚未扩展,因此编译器只能看到源文件中的标点符号。它将参数标识为
int
、COMMA
和int
。每个参数(除了那些其相应参数参与标记连接或字符串化的参数——但是,正如我所说,我不打算在这里讨论那种情况)然后被完全展开。这发生在它们被替换到宏主体之前,因此宏参数的名称不会泄漏到宏之外。所以现在三个参数是
int
、、
和int
。制作了宏主体的拷贝,其中每个参数都替换为相应的(完全扩展的)参数。宏体(标准语中的“替换列表”)是
MC(A B C)
;替换参数后,它变为MC(A , C)
。第 3 步中创建的标记序列被插入到输入中以代替宏调用,然后继续进行预处理。
此时,编译器将看到类函数宏 MC(A, B)
的调用,并将按上述方式进行。然而,这次第一步失败了,因为识别了两个参数,但宏 MC
只有一个参数。
关于c++ - 编译器之间的宏扩展顺序混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44263390/