声明 将名字引入(或再引入)到 C++ 程序中。不是每条声明都实际声明任何东西,且每种实体的声明方式都不同。定义是足以使该名字所标识的实体被使用的声明。
声明是下列之一:
| 属性
|
-
|
(C++11 起) 任意数量属性的序列
|
| 声明符
|
-
|
函数声明符
|
- 此声明必须声明构造函数、析构函数或用户定义的类型转换函数。它只能用作模板声明、显式特化或显式实例化的一部分。
-
-
-
[编辑] 简单声明
简单声明是引入、创建并可能会初始化一个或数个标识符(典型地为变量)的语句。
|
|
声明说明符序列 初始化声明符列表 (可选) ;
|
(1)
|
|
|
|
属性 声明说明符序列 初始化声明符列表;
|
(2)
|
|
|
|
| 属性
|
-
|
(C++11 起) 任何数量属性的序列
|
| 声明说明符序列
|
-
|
说明符 的序列(见下文)
|
| 初始化声明符列表
|
-
|
可以带有初始化器的声明符 的逗号分隔列表。初始化声明符列表 在声明具名的 class/struct/union 或具名枚举时可以省略
|
结构化绑定声明也是简单声明。 (C++17 起)
[编辑] 说明符
声明说明符(声明说明符序列)是下列以空白分隔的说明符的序列,顺序不限:
-
constexpr 说明符,只允许在变量定义,函数及函数模板声明,以及具有字面类型的静态数据成员的声明中使用。
|
(C++11 起) |
-
consteval 说明符,只允许在函数和函数模板声明中使用。
-
constinit 说明符,只允许在拥有静态或线程存储期的变量声明中使用。constexpr、consteval 和 constinit 说明符在一个声明说明符序列 中最多只能出现一个。
|
(C++20 起) |
-
-
-
-
-
- 每个声明说明符序列 中只允许一个类型说明符,但有以下例外:
- const 能与自身外的任何类型说明符组合。
- volatile 能与自身外的任何类型说明符组合。
- signed 或 unsigned 能与 char、long、short 或 int 组合。
- short 或 long 能与 int 组合。
- long 能与 double 组合。
属性可以在声明说明符序列 中出现,此时它们会应用于在它之前的说明符所确定的类型。
在声明说明符序列 中重复任何说明符,如 const static const 或 virtual inline virtual 是错误,但允许 long 出现两次 (C++11 起)。
[编辑] 声明符
初始化声明符列表 是一或多个初始化声明符 的逗号分隔列表,它拥有下列语法:
|
|
| 声明符 初始化器 (可选)
|
(1)
|
|
|
|
| 声明符 requires-子句
|
(2)
|
(C++20 起)
|
|
|
| 声明符
|
-
|
声明符
|
| 初始化器
|
-
|
初始化器(除非在必要的场合,例如初始化引用或 const 对象时,否则为可选)。细节见初始化。
|
| requires-子句
|
-
|
requires 子句,它向函数声明添加约束
|
初始化声明符序列 S D1, D2, D3; 中的每个初始化声明符 均按照如同它是拥有相同说明符的孤立声明来处理:S D1; S D2; S D3;。
每个声明符恰好引入一个对象、引用、函数或(对于 typedef 声明)类型别名,它的类型由声明说明符序列 提供,并且可以被声明符中的运算符,如 &(~的引用)或 [](~的数组)或 ()(返回~的函数)所修饰。可以递归应用这些声明符,如下所示。
声明符 是下列之一:
|
|
| 无限定标识 属性 (可选)
|
(1)
|
|
|
|
| 有限定标识 属性 (可选)
|
(2)
|
|
|
|
... 标识符 属性 (可选)
|
(3)
|
(C++11 起)
|
|
|
* 属性 (可选) cv限定符 (可选) 声明符
|
(4)
|
|
|
|
嵌套名说明符 * 属性 (可选) cv限定符 (可选) 声明符
|
(5)
|
|
|
|
& 属性 (可选) 声明符
|
(6)
|
|
|
|
&& 属性 (可选) 声明符
|
(7)
|
(C++11 起)
|
|
|
非指针声明符 [ 常量表达式 (可选) ] 属性 (可选)
|
(8)
|
|
|
|
非指针声明符 ( 形参列表 ) cv限定符 (可选) 引用限定符 (可选) 异常说明 (可选) 属性 (可选)
|
(9)
|
|
|
|
4) 指针声明符:声明
S * D; 将
D 声明为指向
声明说明符序列 所确定的类型
S 的指针。
6) 左值引用声明符:声明
S & D; 将
D 声明为到
声明说明符序列 所确定的类型
S 的左值引用。
7) 右值引用声明符:声明
S && D; 将
D 声明为到
声明说明符序列 所确定的类型
S 的右值引用。
8) 数组声明符。
非指针声明符 是任意合法声明符,但如果它以 *、& 或 && 起始,那么就必须用括号环绕它。
9) 函数声明符。
非指针声明符 是任意合法声明符,但如果它以 *、& 或 && 起始,那么就必须用括号环绕它。
它可以以尾随返回类型结尾。 (C++11 起)
|
所有情况下,属性 都是属性的序列。当它紧跟标识符之后出现时,它应用于所声明的对象。
|
(C++11 起) |
cv限定符 是 const 与 volatile 限定符的序列,其中任一限定符在序列中至多出现一次。
[编辑] 注解
块声明 出现在代码块内,而当声明中引入的标识符之前已在某个外层代码块中声明时,该代码块的剩余部分中外层声明被隐藏。
如果一个声明引入了一个具有自动存储期的变量,那么它会在执行它的声明语句时被初始化。退出代码块时,所有在该块中声明的自动变量被以它的初始化顺序的相反顺序销毁(与如何退出代码块无关:通过异常、goto 或抵达它的结尾)。
[编辑] 示例
注意,这个例子演示一些复杂声明式是如何依据语言文法进行分析的。其他流行的助记法包括:螺旋规则,从内向外读法,以及声明与使用为镜像等。还有一个自动分析器,位于 https://cdecl.org。
#include <type_traits>
struct S
{
int member;
// 声明说明符序列为 "int"
// 声明符为 "member"
} obj, *pObj(&obj);
// 声明说明符序列为 "struct S { int member; }"
// 声明符 "obj" 声明一个 S 类型的对象
// 声明符 "*pObj" 声明一个 S 的指针,
// 而初始化器 "(&obj)" 为之初始化
int (*(*var1)(double))[3] = nullptr;
// 声明说明符序列为 "int"
// 声明符为 "(*(*var1)(double))[3]"
// 初始化器为 "= nullptr"
// 1. 声明符 "(*(*var1)(double))[3]" 是数组声明符:
// 所声明类型为: [_]的 3 元素数组 "(*(*var1)(double))"
// 2. 声明符 "(*(*var1)(double))" 是指针声明符:
// 所声明类型为: 指向[ [_]的 3 元素数组]的指针 "(*var1)(double)"
// 3. 声明符 "(*var1)(double)" 是函数声明符:
// 所声明类型为: 接受 "(double)" 返回[指向[ [_]的 3 元素数组]的指针]的函数 "(*var1)"
// 4. 声明符 "(*var1)" 是指针声明符:
// 所声明类型为: 指向[接受 "(double)" 返回[指向[ [_]的 3 元素数组]的指针]的函数]的指针 "var1"
// 5. 声明符 "var1" 为一个标识符。
// 此条声明所声明的对象 var1 具有类型“指向接受 "(double)" 返回指向 int 的 3 元素数组的指针的函数的指针”
// 初始化器 "= nullptr" 为此指针提供初值。
// C++11 的替代语法:
auto (*var2)(double) -> int (*)[3] = nullptr;
// 声明说明符序列为 "auto"
// 声明符为 "(*var2)(double) -> int (*)[3]"
// 初始化器为 "= nullptr"
// 1. 声明符 "(*var2)(double) -> int (*)[3]" 是函数声明符:
// 所声明类型为: 接收 "(double)" 返回 "int (*)[3]" 的函数 "(*var2)"
// ...
int main()
{
static_assert(std::is_same_v<decltype(var1), decltype(var2)>);
}
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
| 缺陷报告
|
应用于
|
出版时的行为
|
正确行为
|
| CWG 482
|
C++98
|
重声明中的声明符不能有限定
|
此时允许有限定声明符
|
| CWG 569
|
C++98
|
单独的分号不是合法声明
|
它是空声明,这种声明没有任何效果
|
| CWG 1830
|
C++98
|
允许在声明说明符序列 中重复函数说明符
|
禁止重复
|
[编辑] 参阅