标识符
一个标识符是任意长度的数字、下划线、小写和大写拉丁字母以及大多数 Unicode 字符的序列。
有效标识符的第一个字符必须是以下字符之一
- 大写拉丁字母 A-Z
- 小写拉丁字母 a-z
- 下划线
- 任何具有 Unicode 属性的 Unicode 字符 XID_Start
有效标识符的任何其他字符必须是以下字符之一
- 数字 0-9
- 大写拉丁字母 A-Z
- 小写拉丁字母 a-z
- 下划线
- 任何具有 Unicode 属性的 Unicode 字符 XID_Continue
具有属性 XID_Start 和 XID_Continue 的字符列表可以在 DerivedCoreProperties.txt 中找到。
标识符区分大小写(小写和大写字母是不同的),每个字符都有意义。每个标识符都必须符合 规范形式 C。
注意:大多数实现对 Unicode 标识符的支持有限,例如 gcc(直到 10)。
内容 |
[编辑] 在声明中
标识符可以用于 命名 对象、引用、函数、枚举器、类型、类成员、命名空间、模板、模板特化, 参数包(自 C++11 起) goto 标签和其他实体,以下情况除外
- 用作 关键字 的标识符不能用于其他目的。
|
(自 C++11 起) |
- 用作某些运算符和标点符号的 备用表示 的标识符不能用于其他目的。
|
(自 C++11 起) |
- 标识符出现在标记或预处理标记中(即,不在 user-defined-string-literal 中,例如 operator ""id)(自 C++11 起) 以下形式之一,将被保留
- 在全局命名空间中,以下划线开头的标识符
- 包含双下划线或以下划线后跟大写字母开头的标识符,以下标识符除外
(自 C++11 起) |
|
(自 C++11 起) |
(自 C++20 起) |
“保留”表示标准库头文件 #define 或声明此类标识符以满足其内部需求,编译器可以预定义此类非标准标识符,并且名称重整算法可能会假设这些标识符中的一部分未使用。如果程序员使用此类标识符,则程序格式不正确,不需要诊断。
此外,在翻译单元中 #define 或 #undef 某些名称会导致未定义行为,有关详细信息,请参阅 保留宏名称。
[edit] 僵尸标识符
从 C++14 开始,一些标识符已从 C++ 标准库中删除。它们列在 僵尸名称列表 中。
但是,这些标识符仍然在特定上下文中为以前的标准化保留。删除的成员函数名称不能用作类似函数的宏的名称,其他删除的成员名称不能用作可移植代码中类似对象的宏的名称。
[edit] 在表达式中
命名变量、函数、概念的专用化、(自 C++20 起)或枚举器的标识符可以用作 表达式。仅由标识符组成的表达式的结果是由标识符命名的实体。表达式的 值类别 如果标识符命名函数、变量、模板参数对象(自 C++20 起)或数据成员,则为 左值,否则为 右值(直到 C++11)纯右值(自 C++11 起)(例如,枚举器 为 右值(直到 C++11)纯右值(自 C++11 起)表达式,概念的专用化为 bool 纯右值(自 C++20 起))。
[edit] 类型
标识符表达式的类型与它命名的实体的类型相同。
存在以下例外情况
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 起) |
[edit] 非限定标识符
除了适当地声明的标识符之外,以下内容还可以在表达式中使用相同的角色
- 函数表示法中的 重载运算符 名称,例如 operator+ 或 operator new;
- 函数表示法中的 用户定义的转换函数 名称,例如 operator bool;
|
(自 C++11 起) |
- 后跟其参数列表的 模板 名称,例如 MyTemplate<int>;
- 字符 ~ 后跟类名,例如 ~MyClass;
|
(自 C++11 起) |
|
(自 C++26 起) |
它们与标识符一起被称为 非限定标识符表达式。
[edit] 限定标识符
限定标识符表达式 是在非限定标识符表达式之前加上作用域解析运算符 ::,并且可选地,在作用域解析运算符之间加上一系列以下任何内容
- 命名空间名称;
- 类名;
|
(自 C++11 起) |
|
(自 C++26 起) |
例如,表达式 std::string::npos 是一个表达式,它命名了命名空间 std 中类 string 中的静态成员 npos。表达式 ::tolower 命名了全局命名空间中的函数 tolower。表达式 ::std::cout 命名了命名空间 std 中的全局变量 cout,这是一个顶层命名空间。表达式 boost::signals2::connection 命名了在命名空间 signals2 中声明的类型 connection,该命名空间在命名空间 boost 中声明。
关键字 template 可能会出现在限定标识符中,以根据需要消除 依赖模板名称 的歧义。
有关限定标识符的名称查找的详细信息,请参阅 限定查找。
[edit] 隐式成员访问转换
如果标识符表达式 E 表示某个类 C
的非静态非类型成员,并且满足以下所有条件,则 E 将转换为类成员访问表达式 this->E
- E 为 潜在求值。
-
C
是 E 的最内层封闭类。 -
C
是 E 的最内层封闭类的基类。
此转换不适用于模板定义上下文(请参阅 依赖名称)。
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 } };
[edit] 名称
名称 是使用以下内容之一来引用实体
- 标识符
- 函数表示法中的重载运算符名称 (operator+、operator new)
- 函数表示法中的用户定义的转换函数名称 (operator bool)
|
(自 C++11 起) |
- 后跟其参数列表的模板名称 (MyTemplate<int>)
每个名称都由 声明 引入程序。在多个翻译单元中使用的名称可能引用相同或不同的实体,具体取决于 链接。
当编译器在程序中遇到未知名称时,它会通过 名称查找 将其与引入该名称的声明相关联,除了模板声明和定义中的 依赖名称(对于这些名称,编译器确定它们是命名类型、模板还是其他实体,这可能需要 显式消除歧义)。
[编辑] 缺陷报告
以下行为更改的缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 发布的行为 | 正确行为 |
---|---|---|---|
CWG 1440 | C++11 | :: 之前的 decltype 表达式可以表示任何类型 |
只能表示类 或枚举类型 |
CWG 1963 | C++11 | 除数字、非数字之外的实现定义字符 和通用字符名称可以在标识符中使用 |
禁止 |
CWG 2521 | C++11 | 在 用户定义字符串字面量 中的标识符 字面量运算符的标识符按常规保留 |
规则不同 |
CWG 2771 | C++98 | &a 在类上下文中没有被转换为 &this->a | 它被转换了 |
CWG 2777 | C++20 | 标识符表达式的类型不清楚 如果它命名一个模板参数对象 |
已澄清 |
CWG 2818 | C++98 | 预定义宏名称是保留的 | 它们没有被保留 |
[编辑] 另请参阅
C 文档 用于 标识符
|