Namespaces
Variants

Declarations

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

声明 是将名称引入(或重新引入)C++程序的方式。并非所有声明都会实际声明任何内容,且每种实体的声明方式各不相同。 定义 是指足以使用该名称所标识实体的声明。

声明是以下之一:

(since C++11)
  • 空声明 ( ; )
  • 不带 声明说明符序列 的函数声明:
attr  (可选) declarator ;
attr - (since C++11) 任意数量的 属性 序列
declarator - 函数声明符
此声明必须声明构造函数、析构函数或用户定义类型的 转换函数 。它只能作为 模板声明 显式特化 或显式实例化的一部分使用。
  • 块声明 (可出现在 内部的声明),其本身可以是以下之一:
(自 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 仅可在声明命名类或命名枚举时省略。

结构化绑定声明 同样属于简单声明。

(since C++17)


init-declarator 的语法定义如下:

声明符 初始化器 (1)
声明符 requires-子句  (可选) 合约规范  (可选) (2)
1) 带初始化器的声明符。
2) 不带初始化器的声明符。
declarator - 一个 声明符
initializer - 一个 初始化器
requires-clause - (自 C++20 起) 一个 requires 子句
contract-specs - (自 C++26 起) 一组 函数契约说明符


requires-clause 仅当 declarator 声明的是 模板化函数 时方可出现。

(C++20 起)

contract-specs 仅当 declarator 声明的是函数或函数模板时方可出现。

(C++26 起)

说明符

声明说明符 decl-specifier-seq )是以下空白符分隔的说明符按任意顺序组成的序列:

  • inline 说明符现在也可用于变量声明。
(C++17 起)
  • 类声明和函数声明中允许使用的 friend 说明符。
  • constexpr 说明符,仅允许用于变量定义、函数及函数模板声明,以及字面类型的静态数据成员声明。
(C++11 起)
  • consteval 说明符,仅允许用于函数及函数模板声明。
  • constinit 说明符,仅允许用于具有静态或线程存储期的变量声明。在 constexpr consteval constinit 说明符中,最多只能有一个出现在 decl-specifier-seq 中。
(C++20 起)
  • 存储类说明符 ( register , (直至 C++17) static , thread_local , (自 C++11 起) extern , mutable )。仅允许使用一个存储类说明符 ,但 thread_local 可与 extern static 同时出现 (自 C++11 起)
  • 类型说明符 ( type-specifier-seq ),用于命名类型的说明符序列。声明引入的每个实体的类型均为此类型,可由声明符(见下文)选择性修饰。此说明符序列也用于 类型标识 。仅以下说明符以任意顺序构成 type-specifier-seq
(自 C++11 起)
(自 C++26 起)
(C++17 起)
在声明说明符序列中只允许存在一个类型说明符,以下情况除外:
  • const 可与除自身外的任何类型说明符组合使用。
  • volatile 可与除自身外的任何类型说明符组合使用。
  • signed unsigned 可与 char long short int 组合使用。
  • short long 可与 int 组合使用。
  • long 可与 double 组合使用。
  • long 可与 long 组合使用。
(since C++11)

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

声明说明符序列 中重复任何说明符,例如 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 提供,并可选择性地通过声明符中的运算符进行修改,例如 & (引用)或 [ ] (数组)或 ( ) (返回函数)。如下所示,这些运算符可以递归地应用。

一个 声明符 是以下之一:

非限定标识符 属性  (可选) (1)
限定标识符 属性  (可选) (2)
... 标识符 属性  (可选) (3) (C++11 起)
* 属性  (可选) cv限定符  (可选) 声明符 (4)
嵌套名称说明符 * 属性  (可选) cv限定符  (可选) 声明符 (5)
& 属性  (可选) 声明符 (6)
&& 属性  (可选) 声明符 (7) (C++11 起)
非指针声明符 [ 常量表达式  (可选) ] 属性  (可选) (8)
非指针声明符 ( 形参列表 ) cv限定符  (可选) 引用限定符   (可选) 异常规范  (可选) 属性  (可选) (9)
( 声明符 ) (10)
1) 被声明的 名称
2) 使用 限定标识符 qualified-id )的声明符用于定义或重新声明先前已声明的 命名空间成员 类成员
3) Parameter pack ,仅出现在 函数参数声明 中。
4) 指针声明符 :声明 S * D ; D 声明为指向由 声明说明符序列 S 确定的类型的指针。
5) 指向成员声明 :声明 S C :: * D ; D 声明为指向 C 成员的指针,其类型由 声明说明符序列 S 确定。 嵌套名称说明符 是由 名称和范围解析运算符 :: 组成的序列
6) 左值引用声明符 : 声明 S & D ; D 声明为指向由 声明说明符序列 S 所确定类型的左值引用。
7) 右值引用声明符 :声明 S && D ; D 声明为指向由 声明说明符序列 S 确定的类型的右值引用。
8) 数组声明符 noptr-declarator 可以是任意有效的声明符,但如果它以 *、& 或 && 开头,则必须用括号括起来。
9) 函数声明符 noptr-declarator 可以是任意有效的声明符,但如果它以 *、& 或 && 开头,则必须用括号括起来。 它可以以可选的尾随返回类型结束。 (C++11 起)
10) 括号声明符。

在所有情况下, attr 是一个可选的 属性 序列。当紧接在标识符后出现时,它应用于被声明的对象。

(since C++11)

cv 是由 const 与 volatile 限定符组成的序列,其中每个限定符在该序列中最多出现一次。

注释

块声明 出现在 块内部 时,若声明引入的标识符先前已在外部块中声明,则 外部声明将在该块的剩余部分被隐藏

如果声明引入了一个具有自动存储期的变量,该变量会在其声明语句执行时被初始化。所有在块中声明的自动变量都会在退出该块时被销毁(无论该块是通过 异常 goto 还是通过到达其末尾退出的),销毁顺序与它们的初始化顺序相反。

示例

注意:本示例演示了如何根据语言语法解析某些复杂声明。其他常用的记忆方法包括: 螺旋法则 由内向外阅读法 声明镜像用法 。另有一个自动解析器位于 https://cdecl.org

#include <type_traits>
struct S
{
    int member;
    // 声明说明符序列是 "int"
    // 声明符是 "member"
} obj, *pObj(&obj);
// 声明说明符序列是 "struct S { int member; }"
// 声明符 "obj" 声明了一个 S 类型的对象
// 声明符 "*pObj" 声明了一个指向 S 的指针,
//     且初始化器 "(&obj)" 对其进行了初始化
int i = 1, *p = nullptr, f(), (*pf)(double);
// 声明说明符序列是 "int"
// 声明符 "i" 声明了一个 int 类型的变量,
//     且初始化器 "= 1" 对其进行了初始化
// 声明符 "*p" 声明了一个 int* 类型的变量,
//     且初始化器 "= nullptr" 对其进行了初始化
// 声明符 "f()" 声明(但未定义)
//     一个不接受参数且返回 int 的函数
// 声明符 "(*pf)(double)" 声明了一个指向函数的指针
//     该函数接受 double 参数并返回 int
int (*(*var1)(double))[3] = nullptr;
// 声明说明符序列是 "int"
// 声明符是 "(*(*var1)(double))[3]"
// 初始化器是 "= nullptr"
// 1. 声明符 "(*(*var1)(double))[3]" 是一个数组声明符:
//    声明的类型是:"(*(*var1)(double))" 3个元素的数组
// 2. 声明符 "(*(*var1)(double))" 是一个指针声明符:
//    声明的类型是:"(*var1)(double)" 指向3个元素数组的指针
// 3. 声明符 "(*var1)(double)" 是一个函数声明符:
//    声明的类型是:"(*var1)" 接受 "(double)" 参数的函数,
//    返回指向3个元素数组的指针
// 4. 声明符 "(*var1)" 是一个指针声明符:
//    声明的类型是:"var1" 指向函数的指针,该函数接受 "(double)" 参数,
//    返回指向3个元素数组的指针
// 5. 声明符 "var1" 是一个标识符
// 此声明声明了对象 var1,其类型为"指向函数的指针,
// 该函数接受 double 参数并返回指向3个 int 类型元素数组的指针"
// 初始化器 "= nullptr" 提供了此指针的初始值
// C++11 替代语法:
auto (*var2)(double) -> int (*)[3] = nullptr;
// 声明说明符序列是 "auto"
// 声明符是 "(*var2)(double) -> int (*)[3]"
// 初始化器是 "= nullptr"
// 1. 声明符 "(*var2)(double) -> int (*)[3]" 是一个函数声明符:
//    声明的类型是:"(*var2)" 接受 "(double)" 参数的函数,返回 "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 文档 关于 Declarations