声明 可以把特定的实体引入到 C++ 程序中,并将其与特定的名字关联起来。而将一个实体及其所需要的所有属性都定义下来的声明被称为 定义 。 C++ 程序中的实体包括 值 、 对象 、引用 、 结构化绑定 (C++17 起)、 函数 、 枚举项 、 类型 、 类成员 、 模板 、 模板特化 、 命名空间 和 形参包 等。不同实体的声明方式都有不同。
虽然声明语句多种多样,但我们可以先根据是否可以在 块 内出现将声明分类。
什么是块(Block)?
块即复合语句,指由花括号环绕的语句序列。
能出现在块中的声明包含以下几种:
- 简单声明
- 汇编声明
- 类型别名声明 (C++11 起)
- using 声明
- using 指令
- using enum 声明 (C++20 起)
- 命名空间别名定义
- static assert 声明 (C++11 起)
- 不可见 enum 声明 (C++11 起)
不能出现在块中的声明包含以下几种:
- 函数定义
- 模板声明
- 显式模板实例化
- 显式模板特化
- 命名空间定义
- 链接说明
- 属性声明 (C++11 起)
- 空声明
- 无 声明说明符序列 的函数声明
本篇文章将会详细梳理 简单声明 的内容,其他声明方法将会在梳理相关实体时再进行梳理。
简单声明
简单声明是引入、创建并可能会初始化一个或数个标识符的语句。简单声明的语法结构为:
属性(可选) 声明说明符序列 初始化声明符列表(可选) ;
简单声明的 声明说明符序列 是由用于声明的 说明符 组成的序列,顺序不限。
简单声明的 初始化声明符列表 是由带有初始化器的声明符组成的用逗号分隔的列表。
// 一个声明示例
inline int f();
// 声明说明符序列是 "inline int"
// 声明符 "f()" 声明了一个不接受参数并返回 int 的函数
声明说明符(declaration specifier)
声明说明符作为声明的一部分决定了这个声明的意义,不同的说明符造成的效果往往差异巨大。用一个简单的例子来说明 extern int x, *y, z();
这句代码使用了 extern 以及 int 两个说明符,int 决定了声明实体的类型,而 extern 决定了声明实体的存储期类型。可以按照功能将声明说明符的类型包含以下几种:
- 类型说明符:用于指明声明实体类型的说明符。
- 函数说明符:只能在函数声明中使用的说明符。
- 存储类说明符:用于与作用域一同控制声明实体的 存储时期 与 链接属性。
- typename 说明符:使声明被指定为typedef声明。typedef声明会创建能在任何位置用于替代复杂类型名的别名。
- friend 说明符
- constexpr 说明符
- consteval 说明符
- constinit 说明符
声明符(declarator)
声明中的 初始化声明符列表 是由一个或多个初始化声明符组成的用逗号分隔的列表。初始化声明符包含了 声明符 、 初始化器 以及 requires子句 (C++20 起)。声明符包含下列形式:
无限定标识符 属性(可选)
有限定标识符 属性(可选)
该声明符形式需要提前定义或重声明先前声明的命名空间成员或类成员的声明符。
… 标识符 属性(可选)
该声明符只出现在模板形参包的形参声明中.
* cv限定符(可选) 声明符 属性(可选)
该声明符即指针声明符,用于声明一个指向 声明说明符序列 所确定的类型的指针。
S * D;//将 D 声明为指向类型是 S 的指针。
嵌套名说明符 * 属性(可选) cv限定符(可选) 声明符
该声明符即成员指针声明符,用于声明一个指向声明说明符序列所确定的类型的指针,该类型是嵌套名说明符所确定的类型的成员。
S C::* D;//将 D 声明为指向 C 中的类型是 S 的成员的指针。
& 属性(可选) 声明符
该声明符即左值引用声明符,用于声明一个到声明说明符序列所确定的类型的左值引用。
S & D;//将 D 声明为到类型是 S 的左值引用。
&& 属性(可选) 声明符
该声明符即右值引用声明符,用于声明一个到声明说明符序列所确定的类型的右值引用。
S && D;//将 D 声明为到类型是 S 的右值引用。
非指针声明符 [ 常量表达式(可选) ] 属性(可选)
该声明符即数组声明符,非指针声明符 即任意合法声明符,但如果它以 *、& 或 && 起始,那么就必须用括号环绕它。
非指针声明符 ( 形参列表 ) cv限定符(可选) 引用限定符(可选) 异常说明(可选) 属性(可选)
该声明符即函数声明符,非指针声明符 即任意合法声明符,但如果它以 *、& 或 && 起始,那么就必须用括号环绕它。
每个声明符恰好引入一个对象、引用、函数或类型别名,它的类型由 声明说明符序列 提供,并且可以被声明符中的运算符,如 &(~的引用)或 [](~的数组)或 ()(返回~的函数)所修饰。
// 两个复杂的声明示例
class C
{
std::string member;
// 声明说明符序列是 "std::string"
// 声明符是 "member"
} obj;
// 声明说明符序列是 "class C { std::string member; }"
// 声明符 "obj" 声明了一个类型为 C 的对象
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" 提供此指针的初值。