在 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();
视觉C++首先删除 if 0。Clang 首先扩展 R 原始字符串(并且从不定义 dostuff(。谁是对的,为什么?
[更新:Adrian McCarthy在下面评论说MSVC++ 2017修复了这个问题]
GCC和clang是对的,VC++是错的。
2.2 翻译阶段:
[...]
源文件分解为预处理标记 (2.5( 和空白字符序列(包括注释(。
执行预处理指令,[...]
2.5 预处理令牌 [lex.pptoken]
列出了令牌中的string-literals
。
因此,需要解析来首先标记字符串文字,"使用"#else
和dostuff
函数定义。
我认为值得重申词法分析阶段有趣的"怪癖"。 #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
,它只编译文件。
显然,安全的做法是不要让原始字符串越过预处理器边界。
相关文章:
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 警告处理为错误这里有什么问题
- 什么是原始字符串?
- 如果原始指针被删除shared_ptr,会发生什么情况?
- C++/pimpl:原始指针还是unique_ptr?什么是更好的选择?
- 从原始数据填充成员的正确方法是什么
- 有什么方法可以获取llvm顺从指针值的原始类型(即指针类型)
- 将unique_ptr用于原始指针的正确方法是什么
- C++11 的原始字符串文字 R "(...)" 中括号的基本原理是什么?
- 图像对象和图像原始数据的内容有什么区别
- 什么是C&C++中的原始内存?
- 当函数带两个异常退出时,原始异常发生了什么?
- 原始指针和weak_ptr有什么区别?
- 有什么理由使用原始指针来做RAII吗?c++ 11/14
- 什么时候我想从原始指针构造一个共享指针
- 什么时候应该使用原始指针而不是智能指针
- 在 C++11 中,应该首先发生什么:原始字符串扩展或宏
- 什么会消耗更多的存储空间 - boost::d ynamic_bitset<> 还是原始存储?
- C++ ifstream,ofstream:原始read()/write()调用和以二进制模式打开文件有什么区别?
- 什么是c++中的原始指针?它们与普通指针的区别