【问题标题】:identifier character set (clang)标识符字符集(clang)
【发布时间】:2014-06-28 18:36:33
【问题描述】:

我从不使用 clang。

而我无意中发现了这段代码:

#include <iostream>

void функция(int переменная)
{
    std::cout << переменная << std::endl;
}

int main()
{
    int русская_переменная = 0;
    функция(русская_переменная);
}

将编译正常:http://rextester.com/NFXBL38644 (clang 3.4 (clang++ -Wall -std=c++11 -O2))。

这是一个clang扩展吗?为什么? 谢谢。

UPD:我更想问为什么clang做出这样的决定?因为我从来没有发现有人想要比 c++ 标准现在拥有更多字符的讨论(2.3,rev. 3691)

【问题讨论】:

  • @quantdev 我认为 OP 显然意味着西里尔字母中的符号被接受为these aren't here
  • 它们是西里尔字母,所以它们被标识符的定义所覆盖,还是我遗漏了什么?
  • @Petesh 是的,标识符中允许使用通用字符,我们可以在我对Can you start a class name with a numeric digit? 的回答中找到允许使用的语法......可能还有其他线程也涵盖了这一点。
  • @grisha - 你已经接受了半答案。完整的答案就在它下面。这种现象与两件事有关:(1) 编译器的输入编码(即一种将这些字符输入编译器的方法),以及 (2)标识符中允许的字符。允许使用西里尔字符,因此只需在编译器中使用支持多字节的源文件编码即可。

标签: c++ clang identifier


【解决方案1】:

与其说是扩展,不如说是 Clang 对标准的 多字节字符 部分的解释。 Clang 支持 UTF-8 源代码文件。

至于为什么,我猜“为什么不呢?”是唯一真正的答案;支持更大的字符集对我来说似乎有用且合理。

以下是标准的相关部分(C11 草案):

5.2.1 字符集

1 应定义两组字符及其相关的整理序列:编写源文件的字符集(源字符集),以及在执行环境中解释的字符集(执行字符集)。每个集合进一步分为 基本字符集,其内容由本子条款给出,以及一组零个或多个特定于语言环境的成员(它们不是基本字符集的成员),称为 扩展字符。组合集也称为扩展字符集。执行字符集成员的值是实现定义的。

2 在字符常量或字符串文字中,执行字符集的成员应由源字符集的相应成员或由反斜杠组成的转义序列表示\ 后跟一个或多个字符。一个所有位都设置为 0 的字节,称为 空字符,应存在于基本执行字符集中;它用于终止一个字符串。

3 基本源字符集和基本执行字符集都应具有以下成员:拉丁字母表的 26 个大写字母

A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z

拉丁字母的 26 个小写字母

a b c d e f g h i j k l m
n o p q r s t u v w x y z

十进制数字

0 1 2 3 4 5 6 7 8 9

以下29个图形字符

! " # % & ' ( ) * + , - . / :
; < = > ? [ \ ] ^ _ { | } ~

空格字符和代表水平制表符、垂直制表符和换页符的控制字符。源和执行基本字符集的每个成员的表示应适合一个字节。在源和执行基本字符集中,上述十进制数字列表中0之后的每个字符的值都应比前一个字符的值大一。在源文件中,应该有某种方式来指示每行文本的结尾;本国际标准将这样的行尾指示符视为单个换行符。在基本执行字符集中,应该有代表警告、退格、回车和换行的控制字符。如果在源文件中遇到任何其他字符(标识符、字符常量、字符串文字、标头名称、注释或预处理标记除外) 转换为令牌),行为未定义。

4 字母是大写字母或小写字母,如上定义;在本国际标准中,该术语不包括其他字母表中的其他字符。

5 通用字符名称构造提供了一种命名其他字符的方法。

还有:

5.2.1.2 多字节字符

1 源字符集可能包含多字节字符,用于表示扩展字符集的成员。执行字符集也可能包含多字节字符,这些字符不必具有与源字符集相同的编码。对于这两个字符集,应满足以下条件:

——应存在基本字符集,每个字符应编码为单个字节。

- 任何其他成员的存在、含义和表示都是特定于语言环境的。

——一个多字节字符集可能有一个依赖于状态的编码,其中每个多字节字符序列都以初始移位状态开始并进入其他特定于语言环境的shift states 当在序列中遇到特定的多字节字符时。在初始移位状态下,所有单字节字符都保留其通常的解释并且不会改变移位状态。序列中后续字节的解释是当前移位状态的函数。

——所有位为零的字节应被解释为与移位状态无关的空字符。这样的字节不得作为任何其他多字节字符的一部分出现。

2 对于源文件,应满足以下条件:

——标识符、注释、字符串文字、字符常量或标题名称应以初始移位状态开始和结束。

——标识符、注释、字符串文字、字符常量或标题名称应由一系列有效的多字节字符组成。

【讨论】:

  • 我认为这个问题更像是“标准是否允许任意 Unicode 字符作为标识符”?
  • 标准没有;我说“是的,这是一个扩展”。我会编辑得更清楚。
  • 不允许使用 unicode 标识符是有充分理由的。它甚至是代码高尔夫中的standard loophole
  • @CarlNorum 事实上,C++ 标准确实通过 UCN 允许源代码中的大多数 Unicode 字符。由于 clang 的源编码是 UTF-8,因此标准要求 clang 支持标识符中这些字符的 UTF-8 编码版本。
【解决方案2】:

鉴于 clang 使用 UTF-8 作为源编码,此行为是标准强制要求的:

C++ 定义标识符如下:

标识符:
非数字标识符
标识符标识符非数字
标识符数字
非数字标识符:
非数字
通用字符名称
其他实现定义的字符

这里的重要部分是标识符可以包含通用字符名称。规范还列出了允许的 UCN:

附件 E(规范性)

标识符字符 [charname] 的通用字符名称

E.1 允许的字符范围 [charname.allowed]

00A8, 00AA, 00AD, 00AF, 00B2-00B5, 00B7-00BA, 00BC-00BE, 00C0-00D6, 00D8-00F6, 00F8-00FF

0100-167F, 1681-180D, 180F-1FFF

200B-200D, 202A-202E, 203F-2040, 2054, 2060-206F

2070-218F, 2460-24FF, 2776-2793, 2C00-2DFF, 2E80-2FFF

3004-3007, 3021-302F, 3031-303F

3040-D7FF

F900-FD3D, FD40-FDCF, FDF0-FE44, FE47-FFFD

10000-1FFFD, 20000-2FFFD, 30000-3FFFD, 40000-4FFFD, 50000-5FFFD,
  60000-6FFFD, 70000-7FFFD, 80000-8FFFD, 90000-9FFFD, A0000-AFFFD,
  B0000-BFFFD, C0000-CFFFD, D0000-DFFFD, E0000-EFFFD

标识符中的西里尔字符在 0100-167F 范围内。

C++ 规范进一步要求以与 UCN 相同的方式处理源编码中编码的字符:

任何不在基本源字符集 (2.3) 中的源文件字符都将替换为指定该字符的通用字符名称。 (实现可以使用任何内部编码,只要在源文件中遇到的实际扩展字符,以及在源文件中表示为通用字符名称的相同扩展字符(即,使用 \uXXXX 表示法)是同等处理 — n3337 §2.2 翻译阶段 [lex.phases]/1

因此,鉴于 clang 选择 UTF-8 作为源编码,规范要求将这些字符转换为 UCN(或者使 clang 的行为与执行此类转换无法区分),并且规范允许这些 UCN 出现在标识符中。

它走得更远。表情符号字符恰好在 C++ 规范允许的范围内,因此,如果您看过 Swift code with emoji identifiers 的一些示例并对这种功能感到惊讶,您可能会更惊讶地发现 C++ 具有完全相同的功能:

http://rextester.com/EPYJ41676

http://imgur.com/EN6uanB


另一个可能令人惊讶的事实是,这种行为在 C++11 中并不新鲜。自 C++98 起,C++ 就强制要求这种行为。只是编译器长期忽略了这一点:Clang 在 2013 年的 3.3 版本中实现了此功能。据 this documentation 称,Microsoft Visual Studio 在 2015 年支持此功能。

即使在今天,GCC 6.1 也只支持按字面书写的标识符中的 UCN,并且不遵守其扩展源字符集中的任何字符必须与相应的通用字符名称相同的处理的要求。例如。 gcc 允许 int \u043a\u043e\u0448\043a\u0430 = 10;,但不允许 int кошка = 10;,即使使用 -finput-charset=utf-8。

【讨论】:

    猜你喜欢
    • 2021-07-13
    • 2017-07-17
    • 1970-01-01
    • 2013-01-01
    • 2013-01-28
    • 1970-01-01
    • 1970-01-01
    • 2023-01-23
    • 1970-01-01
    相关资源
    最近更新 更多