标识符
一个 标识符 是由任意长度的数字、下划线、小写和大写拉丁字母,以及大多数 Unicode 字符组成的序列。
一个有效标识符的第一个字符必须是以下之一
- 大写拉丁字母 A-Z
- 小写拉丁字母 a-z
- 下划线
- 任何具有 Unicode 属性 XID_Start 的 Unicode 字符
一个有效标识符的任何其他字符必须是以下之一
- 数字 0-9
- 大写拉丁字母 A-Z
- 小写拉丁字母 a-z
- 下划线
- 任何具有 Unicode 属性 XID_Continue 的 Unicode 字符
具有属性 XID_Start 和 XID_Continue 的字符列表可在 DerivedCoreProperties.txt 中找到。
标识符区分大小写(小写字母和大写字母不同),且每个字符都具有意义。每个标识符必须符合 规范化形式 C。
注意:大多数实现对 Unicode 标识符的支持是有限的,例如 gcc(直到 10 版)。
目录 |
[编辑] 在声明中
标识符可以用于命名对象、引用、函数、枚举器、类型、类成员、命名空间、模板、模板特化,参数包(C++11 起)、goto 标签以及其他实体,但有以下例外:
- 关键字标识符不能用于其他目的。
|
(C++11 起) |
- 运算符和标点符号的替代表示标识符不能用于其他目的。
|
(C++11 起) |
- 以下形式的标识符 (以 token 或预处理 token 形式出现,即不在 user-defined-string-literal 中,如 operator ""id)(C++11 起) 是保留的:
- 在全局命名空间中,以下划线开头的标识符
- 包含双下划线或以下划线后跟大写字母开头的标识符,除了以下标识符:
(C++11 起) |
|
(C++11 起) |
(C++20 起) |
这里的“保留”意味着标准库头文件 #define 或声明此类标识符用于其内部需求,编译器可以预定义此类非标准标识符,并且名称重整算法可能假定其中一些标识符未被使用。如果程序员使用此类标识符,则程序格式错误,不需要诊断。
此外,在翻译单元中 #define 或 #undef 某些名称是未定义行为,详见 保留宏名称。
[编辑] 僵尸标识符
从 C++14 起,一些标识符已从 C++ 标准库中删除。它们列在 僵尸名称列表 中。
然而,这些标识符在特定上下文中仍为之前的标准化所保留。已删除的成员函数名不能用作函数式宏的名称,而其他已删除的成员名不能用作可移植代码中对象式宏的名称。
[编辑] 在表达式中
命名变量、函数、概念的特化(C++20 起)或枚举器的标识符可以用作表达式。仅由标识符组成的表达式的结果是标识符命名的实体。表达式的值类别是 lvalue,如果标识符命名的是函数、变量、模板参数对象(C++20 起)或数据成员,否则为 rvalue(C++11 前)prvalue(C++11 起)(例如,枚举器是 rvalue(C++11 前)prvalue(C++11 起)表达式,概念的特化是布尔 prvalue(C++20 起))。
[编辑] 类型
标识符表达式的类型与其所命名实体的类型相同。
存在以下例外情况:
void f() { float x, &r = x; [=] { decltype(x) y1; // y1 has type float decltype((x)) y2 = y1; // y2 has type float const& because this lambda // is not mutable and x is an lvalue decltype(r) r1 = y1; // r1 has type float& decltype((r)) r2 = y2; // r2 has type float const& }; }
|
(C++11 起) |
[编辑] 非限定标识符
除了适当声明的标识符外,以下内容可以在表达式中扮演相同的角色:
- 函数表示法中的重载运算符名称,例如 operator+ 或 operator new;
- 用户定义转换函数名称,例如 operator bool;
|
(C++11 起) |
- 后跟其参数列表的模板名称,例如 MyTemplate<int>;
- 字符 ~ 后跟类名称,例如 ~MyClass;
|
(C++11 起) |
|
(C++26 起) |
它们与标识符一起被称为 非限定标识符表达式。
[编辑] 限定标识符
一个 限定标识符表达式 是一个非限定标识符表达式,在其前面加上作用域解析运算符 ::,并且可选地,由作用域解析运算符分隔的以下任何序列:
- 命名空间名称;
- 类名称;
|
(C++11 起) |
|
(C++26 起) |
例如,表达式 std::string::npos 是一个表达式,它命名命名空间 std 中类 string 中的静态成员 npos。表达式 ::tolower 命名全局命名空间中的函数 tolower。表达式 ::std::cout 命名命名空间 std 中的全局变量 cout,它是一个顶层命名空间。表达式 boost::signals2::connection 命名在命名空间 signals2 中声明的类型 connection,而 signals2 声明在命名空间 boost 中。
关键字 template 可能出现在限定标识符中,以消除依赖模板名称的歧义。
有关限定标识符名称查找的详细信息,请参阅限定查找。
[编辑] 隐式成员访问转换
如果标识符表达式 E 表示类 C
的某个非静态非类型成员,并且满足以下所有条件,则 E 被转换为类成员访问表达式 this->E
- E 可能被求值。
-
C
是 E 处最内层的 enclosing class。 -
C
是 E 处最内层 enclosing class 的基类。
此转换不适用于模板定义上下文(请参见依赖名称)。
struct X { int x; }; struct B { int b; }; struct D : B { X d; void func() { d; // OK, will be transformed into this->d b; // OK, will be transformed into this->b x; // Error: this->x is ill-formed d.x; // OK, will be transformed into this->d.x // instead of d.this->x or this->d.this->x } };
[编辑] 名称
一个 名称 是以下之一用来指代实体的用法:
- 标识符
- 函数表示法中的重载运算符名称(operator+、operator new)
- 用户定义转换函数名称(operator bool)
|
(C++11 起) |
- 后跟其参数列表的模板名称(MyTemplate<int>)
每个名称都通过声明引入程序。在多个翻译单元中使用的名称可能指代相同或不同的实体,具体取决于链接。
当编译器在程序中遇到未知名称时,它通过名称查找将其与引入该名称的声明关联起来,但模板声明和定义中的依赖名称除外(对于这些名称,编译器确定它们是命名类型、模板还是其他实体,这可能需要显式消歧)。
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 1440 | C++11 | 在 :: 之前的 decltype 表达式可以表示任何类型 |
只能表示类 或枚举类型 |
CWG 1963 | C++11 | 除数字、非数字字符外,实现定义的字符 和通用字符名可以用于标识符 |
已禁止 |
CWG 2521 | C++11 | user-defined-string-literal 中的标识符 字面量运算符如常被保留 |
规则不同 |
CWG 2771 | C++98 | &a 在类上下文中未转换为 &this->a | 它被转换 |
CWG 2777 | C++20 | 标识符表达式的类型不明确 如果它命名了一个模板参数对象 |
已明确 |
CWG 2818 | C++98 | 预定义宏名称被保留 | 它们没有被保留 |
[编辑] 另请参阅
C 文档 关于 标识符
|