在 C++11 中应该先发生什么:原始字符串扩展还是宏?

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

此代码在 Visual C++ 2013 中有效,但在 gcc/clang 中无效:

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

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

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

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

2.2 Phases of translation [lex.phases]:

[...]

  1. The source file is decomposed into preprocessing tokens (2.5) and sequences of white-space characters (including comments).

  2. Preprocessing directives are executed, [...]

2.5预处理标记[lex.pptoken]列出string-literals 在标记中。

因此,首先需要解析以标记字符串文字,“消耗”#elsedostuff 函数定义。

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

#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;

同时添加一个 space 字符(在编译器应该忽略的代码中!)让它编译:

#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,它只会编译文件。

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