声明
声明 是将名称引入(或重新引入) C++ 程序的方式。并非所有声明都真正声明任何内容,并且每种类型的实体的声明方式都不同。定义 是足以使用由名称标识的实体的声明。
声明是以下之一
|
(自 C++11) |
- 空声明(
;
) - 没有 decl-specifier-seq 的函数声明
attr (可选) declarator ; |
|||||||||
attr | - | (自 C++11) 任意数量的 属性 序列 |
declarator | - | 函数声明符 |
- block-declaration(可以在 块 内出现的声明),它反过来可以是以下之一
(自 C++11) |
(自 C++20) | |
(自 C++11) |
- 简单声明
内容 |
[编辑] 简单声明
简单声明是一个语句,它引入、创建并可选地初始化一个或多个标识符,通常是变量。
decl-specifier-seq init-declarator-list (可选) ; |
(1) | ||||||||
attr decl-specifier-seq init-declarator-list; |
(2) | ||||||||
attr | - | (自 C++11) 任意数量的 属性 序列 |
decl-specifier-seq | - | 说明符 序列(见下文) |
init-declarator-list | - | 用逗号分隔的声明符列表,并带可选的初始化器。当声明命名类/结构体/联合体或命名枚举时,init-declarator-list 是可选的 |
一个结构化绑定声明 也是一个简单声明。 (自 C++17 起)
[编辑] 说明符
声明说明符 (decl-specifier-seq) 是以下空格分隔的说明符的序列,可以按任何顺序排列
- the
typedef
说明符。如果存在,整个声明将成为一个typedef 声明,每个声明符将引入一个新的类型名称,而不是对象或函数。 - 函数说明符 (
inline
,virtual
,explicit
),只允许在函数声明 中使用。
|
(自 C++17 起) |
- the
friend
说明符,允许在类和函数声明中使用。
|
(自 C++11) |
(自 C++20) |
- 存储类说明符 (register, (直至 C++17) static, thread_local, (自 C++11 起) extern, mutable)。只允许一个存储类说明符,但 thread_local 可以与 extern 或 static 同时出现 (自 C++11 起)。
- 类型说明符 (type-specifier-seq),一个命名类型的说明符序列。声明引入的每个实体的类型都是此类型,可以选择由声明符(见下文)进行修改。此说明符序列也由类型标识符 使用。以下说明符是 type-specifier-seq 的一部分,可以按任何顺序排列
(自 C++11) | |
(自 C++26 起) |
(自 C++17 起) |
-
- 在 decl-specifier-seq 中只允许一个类型说明符,但以下情况除外
- const 可以与除自身以外的任何类型说明符结合使用。
- volatile 可以与除自身以外的任何类型说明符结合使用。
- signed 或 unsigned 可以与 char、long、short 或 int 结合使用。
- short 或 long 可以与 int 结合使用。
- long 可以与 double 结合使用。
|
(自 C++11) |
属性 可以出现在 decl-specifier-seq 中,在这种情况下,它们将应用于先前说明符确定的类型。
在 decl-specifier-seq 中重复任何说明符,例如 const static const 或 virtual inline virtual 是错误的,但 long 可以出现两次 (自 C++11 起)。
[编辑] 声明符
init-declarator-list 是一个或多个 init-declarator 的逗号分隔序列,它们的语法如下
declarator initializer (可选) | (1) | ||||||||
declarator requires-clause | (2) | (自 C++20) | |||||||
declarator | - | 声明符 |
初始化器 | - | 可选的初始化器(除非强制要求,例如在初始化引用或常量对象时)。有关详细信息,请参见初始化。 |
requires-clause | - | requires-clause,它向函数声明 添加一个约束 |
init-declarator 序列中的每个 init-declarator S D1, D2, D3; 都会被处理,就好像它是一个具有相同说明符的独立声明一样:S D1; S D2; S D3;。
每个声明符都引入一个确切的对象、引用、函数或(对于 typedef 声明)类型别名,其类型由 decl-specifier-seq 提供,并可以选择由声明符中的运算符进行修改,例如 &(引用)或 [](数组)或 ()(函数返回)。这些运算符可以递归地应用,如下所示。
一个 declarator 是以下内容之一
unqualified-id attr (可选) | (1) | ||||||||
qualified-id attr (可选) | (2) | ||||||||
... identifier attr (可选) |
(3) | (自 C++11) | |||||||
* attr (可选) cv (可选) declarator |
(4) | ||||||||
nested-name-specifier * attr (可选) cv (可选) declarator |
(5) | ||||||||
& attr (可选) declarator |
(6) | ||||||||
&& attr (可选) declarator |
(7) | (自 C++11) | |||||||
noptr-declarator [ constexpr (可选) ] attr (可选) |
(8) | ||||||||
noptr-declarator ( parameter-list ) cv (可选) ref (可选) except (可选) attr (可选) |
(9) | ||||||||
D
声明为指向 C
的成员的指针,其类型由 decl-specifier-seq S
确定。 nested-name-specifier 是一个 名称和作用域解析运算符 ::
的序列
在所有情况下,attr 都是 属性 的可选序列。当它出现在标识符之后时,它应用于被声明的对象。 |
(自 C++11) |
cv 是 const 和 volatile 限定符的序列,其中每个限定符最多可以在序列中出现一次。
本节未完善 原因:解释声明名称隐藏规则;如何通过变量/函数声明隐藏具有相同名称的类(但不隐藏 typedef) |
[edit] 注释
当 block-declaration 出现在 块内 时,如果由声明引入的标识符之前已在外部块中声明,则 外部声明将被隐藏,直到块的其余部分。
如果声明引入了一个具有自动存储期的变量,则在执行其声明语句时初始化它。在一个块中声明的所有自动变量在退出块时被销毁(无论块是如何退出的:通过 异常、goto,或者到达其末尾),按其初始化顺序的相反顺序进行。
[edit] 示例
注意:此示例演示了一些复杂声明是如何根据语言语法解析的。其他常用的助记符是:螺旋规则,从 内向外 阅读,以及 声明镜像使用。 还有一个自动化解析器位于 https://cdecl.org。
#include <type_traits> struct S { int member; // decl-specifier-seq is "int" // declarator is "member" } obj, *pObj(&obj); // decl-specifier-seq is "struct S { int member; }" // declarator "obj" declares an object of type S // declarator "*pObj" declares a pointer to S, // and initializer "(&obj)" initializes it int i = 1, *p = nullptr, f(), (*pf)(double); // decl-specifier-seq is "int" // declarator "i" declares a variable of type int, // and initializer "= 1" initializes it // declarator "*p" declares a variable of type int*, // and initializer "= nullptr" initializes it // declarator "f()" declares (but doesn't define) // a function taking no arguments and returning int // declarator "(*pf)(double)" declares a pointer to function // taking double and returning int int (*(*var1)(double))[3] = nullptr; // decl-specifier-seq is "int" // declarator is "(*(*var1)(double))[3]" // initializer is "= nullptr" // 1. declarator "(*(*var1)(double))[3]" is an array declarator: // Type declared is: "(*(*var1)(double))" array of 3 elements // 2. declarator "(*(*var1)(double))" is a pointer declarator: // Type declared is: "(*var1)(double)" pointer to array of 3 elements // 3. declarator "(*var1)(double)" is a function declarator: // Type declared is: "(*var1)" function taking "(double)", // returning pointer to array of 3 elements. // 4. declarator "(*var1)" is a pointer declarator: // Type declared is: "var1" pointer to function taking "(double)", // returning pointer to array of 3 elements. // 5. declarator "var1" is an identifier. // This declaration declares the object var1 of type "pointer to function // taking double and returning pointer to array of 3 elements of type int" // The initializer "= nullptr" provides the initial value of this pointer. // C++11 alternative syntax: auto (*var2)(double) -> int (*)[3] = nullptr; // decl-specifier-seq is "auto" // declarator is "(*var2)(double) -> int (*)[3]" // initializer is "= nullptr" // 1. declarator "(*var2)(double) -> int (*)[3]" is a function declarator: // Type declared is: "(*var2)" function taking "(double)", returning "int (*)[3]" // ... int main() { static_assert(std::is_same_v<decltype(var1), decltype(var2)>); }
[edit] 缺陷报告
以下行为变更缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确的行为 |
---|---|---|---|
CWG 482 | C++98 | 重新声明的声明符不能被限定 | 允许限定声明符 |
CWG 569 | C++98 | 单个独立的分号不是有效的声明 | 这是一个空声明, 它没有效果 |
CWG 1830 | C++98 | 在 decl-specifier-seq 中允许重复函数说明符 | 禁止重复 |
[edit] 另见
C 文档 用于 声明
|