2021 年 7 月更新:

g++ 从 10 版本开始支持 UTF-8 编码的 Unicode 标识符名称,参见这个 bug report

### 与 OIer 的关系 的补充:(为保持我第一篇文章的原样,下文不再修改。)

NOI Linux 2.0发布,其 g++ 版本为 9.3.0,这也是 Ubuntu 20.04 apt 默认安装的版本。至写作时,洛谷的 g++ 版本尚为 8。

注意:本文撰写于文章发布很久以前。仅用来测试显示效果,不一定代表我目前的观点或以后的行文风格。

偶尔看到一份 python 代码里使用了中文变量名,便想,C++ 是否支持 Unicode 标识符呢?

注意本文关注的是 Unicode 标识符(用作变量名等的),而在字符串中放入 Unicode 字符已经是非常普遍的了。

以一个 OIer 具备的能力和条件,可以用 Dev-C++ 写出一段如下代码(保存文件名为 test67.cpp)并尝试编译:

#include <bits/stdc++.h>
using namespace std;
typedef int 整数;
typedef double 双精度小数;

整数 甲;
双精度小数 乙;

int main() {
    cin>>甲>>乙;
    cout<<甲<<" "<<乙<<endl;
    return 0;
}

结果可想而知。放弃。

...But wait!

UPGRADE!

Dev-C++ 是几年前的老古董了?!它所自带的编译器是 TDM-GCC 4.9.2. 现在想要使用 Unicode 标识符,看起来是个很新的概念,很容易想到需要最新的开发环境。

在网上的资料指引下,我决定到 sourceforge 下载 MinGW-w64 的 C++ 编译器。我选择的版本是 x86_64-win32-sjlj. 经过了很多麻烦的步骤,终于搭建好了。

那么接下来就:

g++ test67.cpp

结果又是一大堆 error: stray '\...' in program.

这时源代码文件的字符编码应该还是 GBK,尝试转换为 UTF-8,未果。

上网搜索资料。总结出以下内容:(因为查不到 C++ 标准,只能从各种问答帖中总结)

“标识符只能由字母、数字和下划线组成并不能以下划线开头”已经是很早以前的事了。至少从 C++98 开始,标识符中还允许包含“Universal character name”,我们叫做“通用字符名”。


“通用字符名”包含一些 Unicode 字符码位。从 C++11 开始,这些码位中包括了汉字所在码位。

于是:

g++ test67.cpp -std=c++11

未果。又闻:

Since GCC 5, the option -fextended-identifiers is enabled by default for C++, and for C99 and later C versions. This only supports UCNs in identifiers, not extended characters represented other than with UCNs.

(来自 GCC Wiki

也就是说 GCC(g++)目前只支持 / 你需要写个程序将源代码转换为 像这样的代码:

#include <bits/stdc++.h>
using namespace std;
typedef int \u6574\u6570;
typedef double \u53cc\u7cbe\u5ea6\u5c0f\u6570;

\u6574\u6570 \u7532;
\u53cc\u7cbe\u5ea6\u5c0f\u6570 \u4e59;

int main() {
    cin>>\u7532>>\u4e59;
    cout<<\u7532<<" "<<\u4e59<<endl;
    return 0;
}

Damn! 这显然不是我们想要的。

Compiler matters

既然 GCC 不支持,那我们就换其他编译器!就换 clang 好了。然而在搭建过程中又碰到了比之前更大的麻烦。在此略提一下:

  • Clang 一开始会连许多标准库头文件都找不到。它的 build 出来的默认 target 好像是针对 msvc 的。所以在每个编译命令行中加入 --target=x86_64-w64-mingw。(该选项的值根据之前用到的编译器版本设置)

  • Clang 的 float.h 库和 MinGW64 的不兼容,需要特殊处理

当一切准备完成后:

clang++ -target x86_64-w64-mingw test67.cpp

编译成功!

总结

标准早就已经定了,但实现不实现却还和编译器有关。编译器支持了 Unicode 标识符它还要考虑字符编码的问题。经测试,clang 支持带或不带 BOM 的 UTF-8 编码,不支持 UTF-16 或其他 ANSI 编码。毕竟,“通用字符名”是用 Unicode 码位定义的,编译器应支持一些对 Unicode 的编码,而像将 GBK 编码的源代码转为 UTF-8,应该不是编译器擅长做的事吧。

受限于条件,我未能测试更多的编译器,如 MSVC 等。

与 OIer 的关系

没有关系。 目前,洛谷评测用的是 GCC,而 Codeforces 上已经可以选择用 Clang。(由于我所知有限,不能再举例)目前,国内的官方赛事对 C++11 的标准的支持的进展都非常缓慢,所以还是不要想太多。