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

标签 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 一样?

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

最佳答案

Clang 符合标准; Visual Studio 没有。我认为让 Clang 不符合标准会很麻烦,所以我不会尝试回答“如何让 Clang 像 Visual Studio 一样工作?”。也许这并不是您想知道的。

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

我们将检查调用 MC(int, COMMA, int)。以下是编译器看到标记 MC( 后发生的情况,它们指示宏的调用。

  1. 编译器识别参数是什么,这涉及找到右括号。参数是三个,对应参数个数,这样就OK了。参数尚未扩展,因此编译器只能看到源文件中的标点符号。它将参数标识为 intCOMMAint

  2. 每个参数(除了那些其相应参数参与标记连接或字符串化的参数——但是,正如我所说,我不打算在这里讨论那种情况)然后被完全展开。这发生在它们被替换到宏主体之前,因此宏参数的名称不会泄漏到宏之外。所以现在三个参数是 intint

  3. 制作了宏主体的拷贝,其中每个参数都替换为相应的(完全扩展的)参数。宏体(标准语中的“替换列表”)是 MC(A B C);替换参数后,它变为 MC(A , C)

  4. 第 3 步中创建的标记序列被插入到输入中以代替宏调用,然后继续进行预处理。

此时,编译器将看到类函数宏 MC(A, B) 的调用,并将按上述方式进行。然而,这次第一步失败了,因为识别了两个参数,但宏 MC 只有一个参数。

关于c++ - 编译器之间的宏扩展顺序混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44263390/

相关文章:

rust - 检查宏中定义的项目中的功能标志

c - 如何定义一个宏到一个宏

c++ - 我该怎么做才能让我的代码寻找最少数量的硬币?

javascript - 启用 'Just my code' 时,Visual Studio 2015 JavaScript 调试不会捕获未处理的异常

c++ - 带 Armadillo 的 Eclipse CDT 项目 - CDT 无法识别 'arma' 命名空间

sql-server - D365 的 SSRS 报表开发 - Dynamics 365 报表创作扩展未安装

visual-studio-2015 - 在 Visual Studio 2015 中找不到 time.h(时区、夏令时、tzname)

recursion - 如何避免 Clojure 中每个宏递归的附加括号级别

java - 带分组的拓扑排序

c++ - 在 C++ 中包含头文件时尖括号 < > 和双引号 ""之间的区别?