bar*_*bas 3 c++ g++ visual-c++ clang++ c++14
以下代码在 g++、clang 和 Visual Studio 上编译:
#define HEX(hex_) 0x##hex_
int main()
{
return HEX(BadC0de);
}
Run Code Online (Sandbox Code Playgroud)
与此修改一样,使用 C++14 数字分隔符:
return HEX(1'Bad'C0de);
Run Code Online (Sandbox Code Playgroud)
但这不能在 g++ 或 clang 上编译(它可以在 Visual Studio 上运行):
#define HEX(hex_) 0x##hex_
int main()
{
return HEX(A'Bad'C0de);
}
Run Code Online (Sandbox Code Playgroud)
g++ 输出:
<source>:4:1: warning: multi-character character constant [-Wmultichar]
4 | return HEX(A'Bad'C0de);
| ^
<source>: In function 'int main()':
<source>:4:17: error: expected ';' before user-defined character literal
4 | return HEX(A'Bad'C0de);
| ^~~~~~~~~
<source>:1:25: note: in definition of macro 'HEX'
1 | #define HEX(hex_) 0x##hex_
| ^~~~
<source>:4:17: error: unable to find character literal operator 'operator""C0de' with 'int' argument
4 | return HEX(A'Bad'C0de);
| ^~~~~~~~~
<source>:1:25: note: in definition of macro 'HEX'
1 | #define HEX(hex_) 0x##hex_
| ^~~~
Run Code Online (Sandbox Code Playgroud)
更新:有趣的是,预处理器输出是
return 0xA'Bad'C0de;
Run Code Online (Sandbox Code Playgroud)
它确实可以编译,因此显然独立预处理器的工作方式与统一预处理器不同。
这在 g++/clang 上也会失败,但会出现不同的错误:
return HEX(Bad'C0de);
Run Code Online (Sandbox Code Playgroud)
g++ 输出:
<source>:4:19: warning: missing terminating ' character
4 | return HEX(Bad'C0de);
| ^
<source>:5:2: error: unterminated argument list invoking macro "HEX"
5 | }
| ^
<source>: In function 'int main()':
<source>:4:12: error: 'HEX' was not declared in this scope
4 | return HEX(Bad'C0de);
| ^~~
<source>:4:15: error: expected ';' at end of input
4 | return HEX(Bad'C0de);
| ^
| ;
<source>:4:15: error: expected '}' at end of input
<source>:3:1: note: to match this '{'
3 | {
| ^
Run Code Online (Sandbox Code Playgroud)
更新:在这种情况下,预处理器在解析 HEX() 参数之前停止。
我愿意相信这是一个 g++ bug,但考虑到 Visual Studio 预处理器在历史上的不合规性有多严重,也许这只是一厢情愿的想法。事实上,最后一个程序不仅在 g++ 上失败,还在Visual Studio 上触发内部编译器错误(至少在 godbolt.org 上)!
msvc 输出:
<source>(4): error C2001: newline in constant
<source>(4): fatal error C1057: unexpected end of file in macro expansion
Internal Compiler Error in Z:\opt\compiler-explorer\windows\19.00.24210\bin\amd64\cl.exe. You will be prompted to send an error report to Microsoft later.
INTERNAL COMPILER ERROR in 'Z:\opt\compiler-explorer\windows\19.00.24210\bin\amd64\cl.exe'
Please choose the Technical Support command on the Visual C++
Help menu, or open the Technical Support help file for more information
Run Code Online (Sandbox Code Playgroud)
天真地,我希望所有编译器都将所有文本传递给宏替换,然后再尝试解释其含义(毕竟它是一个预处理器!);只有在 ## 连接之后,我才会期望检查标记的含义。(是的,我知道一些基本的解析恰好匹配括号、方括号等,因此它们中的逗号不会分割参数,但我不希望它扩展到任何其他语言结构。)
标准对这些程序有什么规定吗?它们是否在某种程度上不符合规范,或者它们是否合法并且编译器存在错误?
这是规范中令人讨厌的漏洞之一。预处理器(在规范中)根据“预处理标记”进行定义。输入首先被分成一系列预处理标记,然后对该序列进行宏处理。
现在的问题来自这样一个事实:0xA'Bad'C0de它是单个预处理标记,但A'Bad'C0de不是——它是三个预处理标记(A、'Bad'和C0de),并且标记粘贴运算符##被定义为仅粘贴两个相邻的标记。在这种情况下,标记化阶段取决于已定义的宏以及它们可能执行的操作。
解决这个问题需要进行重大的规范更改,并且需要跟踪直接相邻的预处理标记与非直接相邻的标记(它们之间有空格或注释的标记),并让操作员在有意义的情况下粘贴##其他直接相邻的标记。
这仍然会遇到诸如以下问题HEX(A'B):您如何判断何时)应该是多字符字符常量标记的一部分,而不是结束宏参数列表?
| 归档时间: |
|
| 查看次数: |
178 次 |
| 最近记录: |