声明

来自cppreference.com
< cpp‎ | language

声明将名字引入(或再引入)到 C++ 程序中。每种实体的声明方式都不同。定义是足以使该名字所标识的实体被使用的声明。

声明是下列之一:

(C++11 起)
  • 空声明(;
  • 声明说明符序列 的函数声明:
属性 (可选) 声明符 ;
属性 - (C++11 起) 任意数量属性的序列
声明符 - 函数声明符。
此声明必须声明构造函数、析构函数或用户定义的类型转换函数。它只能用作模板声明显式特化或显式实例化的一部分。
  • 块声明(能在中出现的声明),它可以是下列之一:
(C++11 起)
(C++20 起)
(C++11 起)
  • 简单声明

简单声明

简单声明是引入、创建并可能会初始化一个或数个标识符(典型地为变量)的语句。

声明说明符序列 初始化声明符列表 (可选) ; (1)
属性 声明说明符序列 初始化声明符列表; (2)
属性 - (C++11 起) 任何数量属性的序列
声明说明符序列
(decl-specifier-seq)
- 说明符(specifier)的序列(见下文)
初始化声明符列表
(init-declarator-list)
- 可以带有初始化器(initializer)声明符(declarator)的逗号分隔列表。 初始化声明符列表 在声明具名的 class/struct/union 或具名枚举时可以省略

结构化绑定声明也是简单声明。 (C++17 起)

说明符

声明说明符声明说明符序列)是下列以空白分隔的说明符的序列,顺序不限:

  • inline 说明符也可以用于变量声明。
(C++17 起)
  • friend 说明符,可以在类和函数声明中使用。
  • constexpr 说明符,只允许在变量定义,函数及函数模板声明,以及具有字面类型的静态数据成员的声明中使用。
(C++11 起)
  • consteval 说明符,只允许在函数和函数模板声明中使用。
  • constinit 说明符,只允许在拥有静态或线程存储期的变量声明中使用。constexprconstevalconstinit 说明符在一个 声明说明符序列 中最多只能出现一个。
(C++20 起)
  • 存储类说明符register (C++17 前) staticthread_local (C++11 起)externmutable)。只能使用一个存储类说明符,但 thread_local 可以与 externstatic 一同出现 (C++11 起)
  • 类型说明符类型说明符序列),指名一个类型的说明符的序列。声明所引入的每个实体都是这个类型,并可以被声明符修饰(见下文)。这个说明符序列也用于类型标识(type-id)。只有下列说明符是 类型说明符序列 的一部分,顺序不限:
(C++11 起)
(C++17 起)
每个 声明说明符序列 中只允许一个类型说明符,但有以下例外:
  • const 能与自身外的任何类型说明符组合。
  • volatile 能与自身外的任何类型说明符组合。
  • signedunsigned 能与 charlongshortint 组合。
  • shortlong 能与 int 组合。
  • long 能与 double 组合。
  • long 能与 long 组合。
(C++11 起)

属性可以在 声明说明符序列 中出现,此时它们会应用于在它之前的说明符所确定的类型。

声明说明符序列 中重复任何说明符,如 const static constvirtual 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 起)
* 属性 (可选) 限定符 (可选) 声明符 (4)
嵌套名说明符 * 属性 (可选) 限定符 (可选) 声明符 (5)
& 属性 (可选) 声明符 (6)
&& 属性 (可选) 声明符 (7) (C++11 起)
非指针声明符 [ 常量表达式 (可选) ] 属性 (可选) (8)
非指针声明符 ( 形参列表 ) cv限定符 (可选) 引用限定符 (可选) 异常说明 (可选) 属性 (可选) (9)
1) 所声明的名称
2) 使用有限定的标识符有限定标识)定义或重声明先前声明的命名空间成员类成员的声明符。
3) 形参包,只会在形参声明中出现。
4) 指针声明符:声明 S * D;D 声明为指向 声明说明符序列 所确定的类型 S 的指针。
5) 成员指针声明:声明 S C::* D;D 声明为指向 C 中的类型是 声明说明符序列 所确定的类型 S 的成员的指针。嵌套名说明符名字和作用域解析运算符 :: 的序列
6) 左值引用声明符:声明 S & D;D 声明为到 声明说明符序列 所确定的类型 S 的左值引用。
7) 右值引用声明符:声明 S && D;D 声明为到 声明说明符序列 所确定的类型 S 的右值引用。
8) 数组声明符非指针声明符 是任意合法声明符,但如果它以 *、& 或 && 起始,那么就必须用括号环绕它。
9) 函数声明符非指针声明符 是任意合法声明符,但如果它以 *、& 或 && 起始,那么就必须用括号环绕它。它可以以尾随返回类型结尾。 (C++11 起)

所有情况下,属性 都是属性的序列。当它紧跟标识符之后出现时,它应用于所声明的对象。

(C++11 起)

限定符const 与 volatile 限定符的序列,其中任一限定符在序列中至多出现一次。

注解

块声明出现在代码块内,而当声明中引入的标识符之前已在某个外层代码块中声明时,该代码块的剩余部分中外层声明被隐藏

如果一个声明引入了一个具有自动存储期的变量,那么它会在执行它的声明语句时被初始化。退出代码块时,所有在该块中声明的自动变量被以它的初始化顺序的相反顺序销毁(与如何退出代码块无关:通过异常goto 或抵达它的结尾)。

示例

class C
{
    std::string member; // 声明说明符序列 是 "std::string"
                        // 声明符 是 "member"
} obj, *pObj(&obj);
// 声明说明符序列是 "class C { std::string member; }"
// 声明符 "obj" 定义 C 类型的对象
// 声明符 "*pObj(&obj)" 声明并初始化指向 C 的指针
 
int a = 1, *p = nullptr, f(), (*pf)(double);
// 声明说明符序列是 int
// 声明符 a=1 定义并初始化 int 类型的变量
// 声明符 *p=nullptr 定义并初始化 int* 类型的变量
// 声明符 (f)() 声明(但不定义)不接收参数并返回 int 的函数
// 声明符 (*pf)(double) 定义一个指向接收 double 并返回 int 的函数的指针
 
int (*(*foo)(double))[3] = nullptr;
// 声明说明符序列是 int
// 1. 声明符 "(*(*foo)(double))[3]" 是数组声明符;
//    所声明类型是“含有 3 个 int 元素的数组 /嵌套声明符/ ”
// 2. 嵌套声明符是 "(*(*foo)(double))",它是指针声明符
//    所声明类型是“指向【含有 3 个 int 元素的数组】的指针 /嵌套声明符/”
// 3. 嵌套声明符是 "(*foo)(double)",它是函数声明符
//    所声明类型是“以【指向含有 3 个 int 元素的数组的指针】
//    为返回值的接受一个 double 参数的函数 /嵌套声明符/”
// 4. 嵌套声明符是 "(*foo)",它是(带括号,如函数声明符语法所要求的)指针声明符。
//    所声明类型是“指向【以指向含有 3 个 int 元素的数组的指针
//    为返回值的接受一个 double 参数的函数】的指针 /嵌套声明符/”
// 5. 嵌套声明符是 "foo",它是标识符。
//    该声明声明了对象 foo,它的类型是“指向以指向含有 3 个 int 元素的数组的指针
//    为返回值的接受一个 double 参数的函数的指针”
// 初始化器 "= nullptr" 提供此指针的初值。

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
CWG 482 C++98 重声明中的声明符不能有限定 此时允许有限定声明符
CWG 569 C++98 单独的分号不是合法声明 它是空声明,这种声明没有任何效果
CWG 1830 C++98 允许在 声明说明符序列 中重复函数说明符 禁止重复

参阅