45

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

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

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

4

2 回答 2

38

[更新:下面的 Adrian McCarthy 评论说 MSVC++ 2017 解决了这个问题]

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

2.2 翻译阶段[lex.phases]:

[...]

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

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

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

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

于 2015-06-23T08:12:29.053 回答
1

我认为值得重申词法分析阶段的有趣“怪癖”。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,它只会编译文件。

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

于 2019-01-28T19:09:48.870 回答