声明
声明是在 C++ 程序中引入(或重新引入)名称的方式。并非所有声明都实际声明任何内容,并且每种实体都有不同的声明方式。定义是足以使用名称所标识实体的声明。
声明是以下之一:
|
(C++11 起) |
- 空声明(
;
) - 不带decl-specifier-seq的函数声明
attr (可选) declarator ; |
|||||||||
属性 | - | (C++11 起) 任意数量的属性序列 |
声明符 | - | 一个函数声明符 |
- block-declaration(可以出现在块内部的声明),它可以是以下之一
(C++11 起) |
(C++20 起) | |
(C++11 起) |
- 简单声明
目录 |
[编辑] 简单声明
简单声明是引入、创建并可选地初始化一个或多个标识符(通常是变量)的语句。
decl-specifier-seq init-declarator-list (可选) ; |
(1) | ||||||||
attr decl-specifier-seq init-declarator-list ; |
(2) | (C++11 起) | |||||||
声明说明符序列 | - | 说明符序列 |
init-declarator-list | - | 逗号分隔的init-declarator列表(见下文) |
属性 | - | 任意数量的属性序列 |
只有在声明具名类或具名枚举时才能省略init-declarator-list。
一个结构化绑定声明也是一个简单声明。 |
(C++17 起) |
init-declarator 的语法定义如下
declarator initializer | (1) | ||||||||
declarator requires-clause (可选) contract-specs (可选) | (2) | ||||||||
声明符 | - | 一个声明符 |
初始化器 | - | 一个初始化器 |
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-id。只有以下说明符是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 S D1, D2, D3; 都被处理为如同独立的声明,具有相同的说明符:S D1; S D2; S D3;。
每个声明符恰好引入一个对象、引用、函数,或(对于 typedef 声明)类型别名,其类型由 decl-specifier-seq 提供,并可选地通过声明符中的运算符进行修改,例如 &(引用)、[](数组)或 ()(返回函数)。这些运算符可以递归应用,如下所示。
一个声明符是以下之一
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
声明为指向类型由 decl-specifier-seq S
确定的 C
的成员的指针。nested-name-specifier 是名称和作用域解析运算符 ::
的序列
在所有情况下,attr 是可选的属性序列。当紧跟在标识符之后时,它适用于被声明的对象。 |
(C++11 起) |
cv 是const 和 volatile 限定符的序列,其中每个限定符在序列中最多只能出现一次。
本节不完整 原因:解释声明名称隐藏规则;变量/函数声明如何隐藏同名类(但不隐藏 typedef) |
[编辑] 注意
当块声明出现在块内时,如果声明引入的标识符先前已在外部块中声明,则外部声明在块的其余部分被隐藏。
如果声明引入的变量具有自动存储期,则在其声明语句执行时进行初始化。在一个块中声明的所有自动变量在该块退出时(无论该块如何退出:通过异常、goto,或到达其末尾)以与初始化顺序相反的顺序销毁。
[编辑] 示例
注意:本示例演示了如何根据语言语法解析一些复杂的声明。其他流行的助记符包括:螺旋规则,由内而外阅读,以及声明镜像使用。还有一个自动解析器在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)>); }
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 482 | C++98 | 重声明的声明符不能限定 | 允许限定声明符 |
CWG 569 | C++98 | 一个单独的分号不是有效的声明 | 它是一个空声明, 没有效果 |
CWG 1830 | C++98 | 允许在decl-specifier-seq中重复函数说明符 | 禁止重复 |
[编辑] 参见
C 文档 关于 声明
|