声明

来自cppreference.com
< cpp‎ | language

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

声明是下列之一:

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

目录

[编辑] 简单声明

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

decl-specifier-seq init-declarator-list(可选) ; (1)
attr decl-specifier-seq init-declarator-list; (2)
attr (C++11 起) - 任意数量属性的序列
decl-specifier-seq - 指定符序列(见后述)。
init-declarator-list - 声明器带可选的初始化器的逗号分隔列表。 init-declarator-list 在声明具名的 class/struct/union 或具名枚举时是可选的

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

[编辑] 指定符

声明指定符decl-specifier-seq )是下列空白符分隔的指定符的任意顺序序列:

  • inline 指定符亦允许在变量声明上。
(C++17 起)
  • friend 指定符,在类及函数声明中允许。
  • constexpr 指定符,仅在变量定义、函数及函数模板声明与字面类型的静态数据成员声明中允许。
  • 存储类指定符registerstaticthread_local (C++11 起)externmutable )。仅允许一个存储类指定符,除了 thread_local 可以与 externstatic 一同出现。
  • 类型指定符type-specifier-seq ),指明类型的指定符序列。声明所引入的每个实体为此类型,可选地为声明器所修饰(见后述)。此指定符序列亦为类型 id 所用。唯有下列指定符是 type-specifier-seq 的一部分,顺序任意:
(C++11 起)
(C++17 起)
一个 decl-specifier-seq 中只允许一个类型指定符,除了下列例外:
- const 能与自身外的任何类型指定符组合。
- volatile 能与自身外的任何类型指定符组合。
- signedunsigned 能与 charlongshortint 组合。
- shortlong 能与 int 组合。
- long 能与 double 组合。
- long 能与 long 组合。
(C++11 起)

属性可出现于 decl-specifier-seq 中,该情况下它们应用到前附指定符所确定的类型。

decl-specifier-seq 中仅有的允许出现二次的指定符是 long (可以在行中出现二次)。所有其他重复,例如 const static constvirtual inline virtual 均为错误。

(C++17 起)

[编辑] 声明器

init-declarator-seq 是一或多个 init-declarators 的逗号分隔列表,它拥有下列语法:

declarator initializer(可选) (1)
declarator requires-clause (2) (C++20 起)
declarator - 声明器
initializer - 可选的初始化器(除非在要求的场合,例如初始化引用或 const 对象时)。细节见初始化
requires-clause(C++20) - 添加制约函数声明

每个 init-declarator S D1, D2, D3; 序列中的 init-declaractor 被处理为如同它是拥有同一指定符的孤立声明: S D1; S D2; S D3;

每个声明器恰好引入一个对象、引用、函数或(对于 typedef 声明)类型别名,其类型为 decl-specifier-seq 所提供,并可选地为声明器中的运算符,如 & (到它的引用)或 [] (它的数组)或 () (返回它的函数)所修饰。这些声明器能递归应用,如下所示。

declarator 是下列之一:

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)
1) 所声明的名称
2) 使用有限定的标识符qualified-id )定义或重声明先前声明的命名空间成员类成员的声明器。
3) 参数包,仅出现于参数声明
4) 指针声明器:声明 S * D; 声明 D 为指向 decl-specifier-seq 所确定的类型 S 的指针。
5) 指向成员指针声明:声明 S C::* D; 声明 D 为指向 C 的类型为 decl-specifier-seq 所确定的类型 S 的成员指针。 nested-name-specifier名称及作用域解析运算符 :: 的序列
6) 左值引用声明器:声明 S & D; 声明 D 为到 decl-specifier-seq 所确定的类型 S 的左值引用。
7) 右值引用声明器:声明 S && D; 声明 D 为到 decl-specifier-seq 所确定的类型 S 的右值引用。
8) 数组声明器noptr-declarator 为任何合法声明器,但若它以 * 、 & 或 && 起始,则它必须为括号所环绕。
9) 函数声明器noptr-declarator 为任何合法声明器,但若它以 * 、 & 或 && 起始,则它必须为括号所环绕。注意最外层函数声明器能以可选的尾随返回类型结尾。

所有情况下, attr 均为属性的可选序列。在立即出现于标识符后时,它应用到被声明的对象。

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

[编辑] 注意

块声明出现在块内,并引入先前声明于外层块的标识符时,块的剩余部分中外层声明被隐藏

若声明引入拥有自动存储期的变量,则它在执行其声明语句时初始化。退出块时所有声明于块中的自动变量被以其初始化顺序的逆序销毁(无关乎如何退出块:通过异常goto 或抵达其结尾)。

[编辑] 示例

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