Declarations
声明 是将名称引入(或重新引入)C++程序的方式。并非所有声明都会实际声明任何内容,且每种实体的声明方式各不相同。 定义 是指足以使用该名称所标识实体的声明。
声明是以下之一:
|
(since C++11) |
-
空声明 (
;) - 不带 声明说明符序列 的函数声明:
attr
(可选)
declarator
;
|
|||||||||
| attr | - | (since C++11) 任意数量的 属性 序列 |
| declarator | - | 函数声明符 |
- 块声明 (可出现在 块 内部的声明),其本身可以是以下之一:
| (自 C++11 起) |
| (自 C++20 起) | |
| (自 C++11 起) |
-
- 简单声明
目录 |
简单声明
简单声明是一种引入、创建并可选地初始化一个或多个标识符(通常是变量)的语句。
decl-specifier-seq
init-declarator-list
(可选)
;
|
(1) | ||||||||
attr
decl-specifier-seq
init-declarator-list
;
|
(2) | (C++11 起) | |||||||
| decl-specifier-seq | - | 说明符序列 |
| init-declarator-list | - | 以逗号分隔的 init-declarator 列表(见下文) |
| attr | - | 任意数量的 属性 序列 |
init-declarator-list
仅可在声明命名类或命名枚举时省略。
|
结构化绑定声明 同样属于简单声明。 |
(since C++17) |
init-declarator
的语法定义如下:
| 声明符 初始化器 | (1) | ||||||||
| 声明符 requires-子句 (可选) 合约规范 (可选) | (2) | ||||||||
| declarator | - | 一个 声明符 |
| initializer | - | 一个 初始化器 |
| requires-clause | - | (自 C++20 起) 一个 requires 子句 |
| contract-specs | - | (自 C++26 起) 一组 函数契约说明符 |
|
requires-clause 仅当 declarator 声明的是 模板化函数 时方可出现。 |
(C++20 起) |
|
contract-specs 仅当 declarator 声明的是函数或函数模板时方可出现。 |
(C++26 起) |
说明符
声明说明符 ( decl-specifier-seq )是以下空白符分隔的说明符按任意顺序组成的序列:
-
typedef说明符。如果存在,整个声明即为 typedef声明 ,且每个声明符引入的是新类型名称而非对象或函数。 -
函数说明符(
inline、virtual、explicit),仅允许在 函数声明 中使用。
|
(C++17 起) |
-
类声明和函数声明中允许使用的
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 起) |
-
-
在声明说明符序列中只允许存在一个类型说明符,以下情况除外:
- const 可与除自身外的任何类型说明符组合使用。
- volatile 可与除自身外的任何类型说明符组合使用。
- signed 或 unsigned 可与 char 、 long 、 short 或 int 组合使用。
- short 或 long 可与 int 组合使用。
- long 可与 double 组合使用。
|
(since C++11) |
属性 可以出现在 声明说明符序列 中,此时它们适用于由前序说明符确定的类型。
在 声明说明符序列 中重复任何说明符,例如 const static const 或 virtual inline virtual 均属于错误 ,但允许 long 出现两次的情况除外 (自 C++11 起) 。
声明符
每个 init-declarator-list 中的 init-declarator S D1, D2, D3 ; 都会被视为具有相同说明符的独立声明进行处理: S D1 ; S D2 ; S D3 ; 。
每个声明符精确引入一个对象、引用、函数或(对于typedef声明)类型别名,其类型由 decl-specifier-seq 提供,并可选择性地通过声明符中的运算符进行修改,例如 & (引用)或 [ ] (数组)或 ( ) (返回函数)。如下所示,这些运算符可以递归地应用。
一个 声明符 是以下之一:
| 非限定标识符 属性 (可选) | (1) | ||||||||
| 限定标识符 属性 (可选) | (2) | ||||||||
...
标识符
属性
(可选)
|
(3) | (C++11 起) | |||||||
*
属性
(可选)
cv限定符
(可选)
声明符
|
(4) | ||||||||
嵌套名称说明符
*
属性
(可选)
cv限定符
(可选)
声明符
|
(5) | ||||||||
&
属性
(可选)
声明符
|
(6) | ||||||||
&&
属性
(可选)
声明符
|
(7) | (C++11 起) | |||||||
非指针声明符
[
常量表达式
(可选)
]
属性
(可选)
|
(8) | ||||||||
非指针声明符
(
形参列表
)
cv限定符
(可选)
引用限定符
(可选)
异常规范
(可选)
属性
(可选)
|
(9) | ||||||||
(
声明符
)
|
(10) | ||||||||
|
在所有情况下, attr 是一个可选的 属性 序列。当紧接在标识符后出现时,它应用于被声明的对象。 |
(since C++11) |
cv 是由 const 与 volatile 限定符组成的序列,其中每个限定符在该序列中最多出现一次。
|
此章节内容不完整
原因:需说明声明名称隐藏规则;解释变量/函数声明如何隐藏同名类(但不隐藏typedef) |
注释
当 块声明 出现在 块内部 时,若声明引入的标识符先前已在外部块中声明,则 外部声明将在该块的剩余部分被隐藏 。
如果声明引入了一个具有自动存储期的变量,该变量会在其声明语句执行时被初始化。所有在块中声明的自动变量都会在退出该块时被销毁(无论该块是通过 异常 、 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 i = 1, *p = nullptr, f(), (*pf)(double); // 声明说明符序列是 "int" // 声明符 "i" 声明了一个 int 类型的变量, // 且初始化器 "= 1" 对其进行了初始化 // 声明符 "*p" 声明了一个 int* 类型的变量, // 且初始化器 "= nullptr" 对其进行了初始化 // 声明符 "f()" 声明(但未定义) // 一个不接受参数且返回 int 的函数 // 声明符 "(*pf)(double)" 声明了一个指向函数的指针 // 该函数接受 double 参数并返回 int int (*(*var1)(double))[3] = nullptr; // 声明说明符序列是 "int" // 声明符是 "(*(*var1)(double))[3]" // 初始化器是 "= nullptr" // 1. 声明符 "(*(*var1)(double))[3]" 是一个数组声明符: // 声明的类型是:"(*(*var1)(double))" 3个元素的数组 // 2. 声明符 "(*(*var1)(double))" 是一个指针声明符: // 声明的类型是:"(*var1)(double)" 指向3个元素数组的指针 // 3. 声明符 "(*var1)(double)" 是一个函数声明符: // 声明的类型是:"(*var1)" 接受 "(double)" 参数的函数, // 返回指向3个元素数组的指针 // 4. 声明符 "(*var1)" 是一个指针声明符: // 声明的类型是:"var1" 指向函数的指针,该函数接受 "(double)" 参数, // 返回指向3个元素数组的指针 // 5. 声明符 "var1" 是一个标识符 // 此声明声明了对象 var1,其类型为"指向函数的指针, // 该函数接受 double 参数并返回指向3个 int 类型元素数组的指针" // 初始化器 "= nullptr" 提供了此指针的初始值 // C++11 替代语法: auto (*var2)(double) -> int (*)[3] = nullptr; // 声明说明符序列是 "auto" // 声明符是 "(*var2)(double) -> int (*)[3]" // 初始化器是 "= nullptr" // 1. 声明符 "(*var2)(double) -> int (*)[3]" 是一个函数声明符: // 声明的类型是:"(*var2)" 接受 "(double)" 参数的函数,返回 "int (*)[3]" // ... 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 | 允许在 decl-specifier-seq 中重复函数说明符 | 禁止重复 |
参见
|
C 文档
关于
Declarations
|