此代码适用于 Visual C++ 2013,但不适用于 gcc/clang:
#if 0
R"foo(
#else
int dostuff () { return 23; }
// )foo";
#endif
dostuff();
Visual C++ 首先删除 if 0。Clang 首先扩展 R 原始字符串(并且从不定义 dostuff)。谁是对的,为什么?
此代码适用于 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 翻译阶段[lex.phases]:
[...]
源文件被分解为预处理标记(2.5)和空白字符序列(包括注释)。
执行预处理指令,[...]
并且2.5 预处理标记 [lex.pptoken]在标记中列出string-literals
。
因此,首先需要解析来标记字符串文字,“使用” #else
anddostuff
函数定义。
我认为值得重申词法分析阶段的有趣“怪癖”。a 中的内容不会像您天真的想象的那样#if 0 ... #else
被忽略(在我测试它之前我很天真)。这里有两个示例,区别只是块内的原始字符串声明中的R
和之间的额外空格。"
#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;
在添加空格字符时(在编译器应该忽略的代码中!)让它编译:
#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
,它只会编译文件。
显然,安全的做法是不要让您的原始字符串跨越预处理器边界。