声明
声明 是将名字引入(或重新引入) C++ 程序的方式。并非所有声明实际声明任何东西,并且每种实体的声明方式都不同。定义 是足以使用由名字标识的实体的声明。
声明是下列之一
|
(C++11 起) |
- 空声明 (
;
) - 无 decl-specifier-seq 的函数声明
attr (可选) 声明符 ; |
|||||||||
attr | - | (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 起) | |||||||
decl-specifier-seq | - | 说明符序列 |
init-declarator-list | - | 逗号分隔的 init-declarator 列表(见下文) |
attr | - | 任意数量的 属性 序列 |
仅当声明具名类或具名枚举时,才能省略 init-declarator-list。
结构化绑定声明 也是简单声明。 |
(C++17 起) |
init-declarator 的语法定义如下
声明符 初始化器 | (1) | ||||||||
声明符 requires-clause (可选) contract-specs (可选) | (2) | ||||||||
声明符 | - | 声明符 |
初始化器 | - | 初始化器 |
requires-clause | - | (C++20 起) requires 子句 |
contract-specs | - | (C++26 起) 函数契约说明符列表 |
仅当 声明符 声明模板函数时,才能出现 requires-clause。 |
(C++20 起) |
仅当 声明符 声明函数或函数模板时,才能出现 contract-specs。 |
(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) | ||||||||
... 标识符 attr (可选) |
(3) | (C++11 起) | |||||||
* attr (可选) cv (可选) 声明符 |
(4) | ||||||||
nested-name-specifier * attr (可选) cv (可选) 声明符 |
(5) | ||||||||
& attr (可选) 声明符 |
(6) | ||||||||
&& attr (可选) 声明符 |
(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) |
[编辑] 注意
当 block-declaration 出现在块内,并且声明引入的标识符先前在外部块中声明时,外部声明在该块的剩余部分中被隐藏。
如果声明引入具有自动存储期的变量,则在执行其声明语句时将其初始化。在块中声明的所有自动变量都在退出块时销毁(无论块如何退出:通过异常、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++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 482 | C++98 | 重声明的声明符不能被限定 | 允许限定声明符 |
CWG 569 | C++98 | 单独的分号不是有效的声明 | 它是一个空声明, 没有效果 |
CWG 1830 | C++98 | 在 decl-specifier-seq 中允许重复函数说明符 | 禁止重复 |
[编辑] 参见
C 文档 关于 声明
|