C++ 为什么使用UTF8在字符串文字中字符串化欧元符号不会生成UCN?

C++ 为什么使用UTF8在字符串文字中字符串化欧元符号不会生成UCN?,c++,c-preprocessor,C++,C Preprocessor,规范说在编译的第一阶段 任何不在基本源字符集(2.3)中的源文件字符将替换为指定该字符的通用字符名 在第四阶段,它说 执行预处理指令,扩展宏调用 在第五阶段,我们有 字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名,都将转换为执行字符集的相应成员 对于#操作符,我们有 在字符文字或字符串文字的每个“和\字符之前插入\字符(包括分隔”字符) 因此,我进行了以下测试 #define GET_UCN(X) #X GET_UCN("€") 使用UTF

规范说在编译的第一阶段

任何不在基本源字符集(2.3)中的源文件字符将替换为指定该字符的通用字符名

在第四阶段,它说

执行预处理指令,扩展宏调用

在第五阶段,我们有

字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名,都将转换为执行字符集的相应成员

对于
#
操作符,我们有

在字符文字或字符串文字的每个
\
字符之前插入
\
字符(包括分隔
字符)

因此,我进行了以下测试

#define GET_UCN(X) #X
GET_UCN("€")

使用UTF-8的输入字符集(匹配我的文件编码),我希望
\X
操作的预处理结果如下:
“\\\u20AC\
。GCC、Clang和boost.wave不会将
转换为UCN,而是产生
“\”€\
。我觉得我错过了什么。你能解释一下吗?

我想你会发现欧元符号不符合条件
任何不在基本源字符集中的源文件字符
,因此你引用的其余文本不适用

使用您最喜欢的二进制编辑器打开测试文件,检查用于表示欧元符号的值是什么
GET_UCN(€“
”,以及字符文本或非原始字符串文本中的通用字符名是否转换为执行字符集的相应成员

过去是

“或将字符文字和字符串文字中的通用字符名转换为执行字符集的成员”


也许你需要一个未来版本的g++。

我不确定你是从哪里得到翻译阶段1的引文的,§5.1.1.2/1中提到了翻译阶段1:

如有必要,将物理源文件多字节字符以实现定义的方式映射到源字符集(为行尾指示符引入新行字符)。三角图序列被相应的单字符内部表示替换


因此,在本例中,欧元字符€(在UTF-8中表示为多字节序列E282 AC)被映射到执行字符集,该字符集也恰好是UTF-8,因此其表示形式保持不变。它不会被转换成通用字符名,因为没有任何规定它应该这样做。

这只是一个bug。§2.1/1说明了第1阶段

(实现可以使用任何内部编码,只要源文件中遇到的实际扩展字符和源文件中表示为通用字符名的相同扩展字符(即使用\uxxx表示法)被等效处理。)

这不是注释或脚注。C++0x为原始字符串文本添加了一个异常,如果您有一个异常,它可能会解决您手头的问题

该程序清楚地显示了故障:

#include <iostream>

#define GET_UCN(X) L ## #X

int main() {
std::wcout << GET_UCN("€") << '\n' << GET_UCN("\u20AC") << '\n';
}
#包括
#定义GET_UCN(X)L####X
int main(){

std::wcout这似乎是预处理器中的一个错误。您是否假设源字符集是UTF-32?源字符集似乎是实现定义的。值得一提的是,我实现了一个预处理器,它正确地实现了这一点:不,“基本源字符集”由C标准定义为仅包括26个大写字母、26个小写字母、10位数字、29个图形字符、空格、水平制表符、垂直制表符、换页,以及“以某种方式指示每行文本的结尾”(C99§5.2.1)“@adam抱歉我笨拙地表达了自己。我说的是Johanes使用的编译器实际在做什么,而不是标准要求它应该做什么。尽管如此,我还是说了“我怀疑”如果Johannes的观点符合标准的话,我会很高兴地承认我的怀疑是错误的。C和C++标准在这一领域有不同之处。C是这样设计的,即原始编码可以被保留,C++假设转换为UCN。C99标准与什么有关?不要太苛刻。这个问题提到GCC不是G++。只有一个标签提到C++,而不是C.UH-OH,删除一个小空白,我发明了<代码>·Clang >和代码>操作符…但是为什么EDG/COMO,GCC,Boost和Boo.Wave都有相同的bug?@约翰尼斯:因为它是一个可能发生的bug。我想他们都认为第1阶段是EA。他们可能太聪明了。我对Clang感到非常惊讶,因为不久前我看了它一眼,它的Unicode支持类看起来很可靠。如果这仍然不能令人满意,请注意前端需要一个单独的例程专门用于将令牌串入Unicode。除此之外,实现没有生成e通用字符名。(换句话说,您提到的实现都省略了此函数。)现在,通用字符名对用户来说是完全无用的,字符串化主要用于诊断消息,修复此错误的主要效果是为那些在标识符中实际使用Unicode的勇敢程序员扰乱
assert
-风格的消息。啊,通过“将令牌字符串化为Unicode”我还提到了一个事实,即大多数实现都设计用于非Unicode系统,例如ISO 8859,因此源代码不能简单地转换为十六进制,而是需要实际的格式转换。即使源字符集和执行字符集都不是Unicode。顺便提一下,您在c上询问了答案别忘了戴帽子