在 C++11 中,应该首先发生什么:原始字符串扩展或宏

In C++11 what should happen first: raw string expansion or macros?

本文关键字:原始 什么 字符串 扩展 C++11      更新时间:2023-10-16

此代码适用于Visual C++ 2013,但不适用于gcc/clang:

#if 0
R"foo(
#else
int dostuff () { return 23; }
// )foo";
#endif
dostuff();

视觉C++首先删除 if 0。Clang 首先扩展 R 原始字符串(并且从不定义 dostuff(。谁是对的,为什么?

[更新:Adrian McCarthy在下面评论说MSVC++ 2017修复了这个问题]

GCC和clang是对的,VC++是错的。

2.2 翻译阶段:

[...]

  1. 源文件分解为预处理标记 (2.5( 和空白字符序列(包括注释(。

  2. 执行预处理指令,[...]

2.5 预处理令牌 [lex.pptoken] 列出了令牌中的string-literals

因此,需要解析来首先标记字符串文字,"使用"#elsedostuff函数定义。

我认为值得重申词法分析阶段有趣的"怪癖"。 #if 0 ... #else内的内容并不像你天真想象的那样被忽略(在我测试它之前我很天真(。 这里有两个例子,区别只是#if 0内的原始字符串声明中的R"之间的额外空格。

#include <iostream>
using namespace std;
#if 0 
const char* s = R"(
#else
int foo() { return 3; }
// )";
#endif
int main() {
    std::cout << foo() << std::endl;
    return 0;
}

结果在 (GCC 6.3, C++14(

prog.cpp: In function ‘int main()’:
prog.cpp:12:19: error: ‘foo’ was not declared in this scope
  std::cout << foo() << std::endl;

添加空格字符(在编译器应该忽略的代码中!(时,可以编译:

#include <iostream>
using namespace std;
#if 0 
const char* s = R "(
#else
int foo() { return 3; }
// )";
#endif
int main() {
    std::cout << foo() << std::endl;
    return 0;
}

编译和运行

3

请注意,使用传统的非原始字符串文本不会有此问题。不允许跨换行符拆分非原始字符串,因此在这种情况下,将忽略非原始字符串,并且不会对其进行标记化。所以如果你去掉了R,它只编译文件。

显然,安全的做法是不要让原始字符串越过预处理器边界。