命名空间
变体
操作

声明

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
 
 

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

声明是以下之一:

  • 属性声明(attr ;
(C++11 起)
  • 空声明(;
  • 不带decl-specifier-seq的函数声明
attr (可选) declarator ;
属性 - (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 起)
声明说明符序列 - 说明符序列
init-declarator-list - 逗号分隔的init-declarator列表(见下文)
属性 - 任意数量的属性序列


只有在声明具名类或具名枚举时才能省略init-declarator-list

一个结构化绑定声明也是一个简单声明。

(C++17 起)


init-declarator 的语法定义如下

declarator initializer (1)
declarator requires-clause (可选) contract-specs (可选) (2)
1) 带有初始化器的声明符。
2) 不带初始化器的声明符。
声明符 - 一个声明符
初始化器 - 一个初始化器
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 说明符,只允许用于具有静态或线程存储期变量的声明。在decl-specifier-seq中,constexprconstevalconstinit 说明符中最多只能出现一个。
(C++20 起)
  • 存储类说明符register(C++17 前) staticthread_local(C++11 起) externmutable)。只允许一个存储类说明符,除了 thread_local 可以与 externstatic 一起出现(C++11 起)
  • 类型说明符type-specifier-seq),是命名类型的说明符序列。声明引入的每个实体的类型就是此类型,可选地由声明符修改(见下文)。此说明符序列也用于type-id。只有以下说明符是type-specifier-seq的一部分,可以按任意顺序排列
(C++11 起)
(C++26 起)
(C++17 起)
在一个 decl-specifier-seq 中只允许一个类型说明符,但以下情况除外:
  • const 可以与除自身以外的任何类型说明符组合。
  • volatile 可以与除自身以外的任何类型说明符组合。
  • signedunsigned 可以与 charlongshortint 组合。
  • shortlong 可以与 int 组合。
  • long 可以与 double 组合。
  • long 可以与 long 组合。
(C++11 起)

属性可以出现在decl-specifier-seq中,在这种情况下,它们适用于由前面的说明符确定的类型。

decl-specifier-seq 中任何说明符的重复,例如 const static constvirtual 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)
... 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 声明为指向类型由 decl-specifier-seq S 确定的 C 的成员的指针。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 任何有效的声明符,但如果它以 *、& 或 && 开头,则必须用括号括起来。它可以以可选的尾置返回类型结束。(C++11 起)

在所有情况下,attr 是可选的属性序列。当紧跟在标识符之后时,它适用于被声明的对象。

(C++11 起)

cvconst 和 volatile 限定符的序列,其中每个限定符在序列中最多只能出现一次。

[编辑] 注意

块声明出现在块内时,如果声明引入的标识符先前已在外部块中声明,则外部声明在块的其余部分被隐藏

如果声明引入的变量具有自动存储期,则在其声明语句执行时进行初始化。在一个块中声明的所有自动变量在该块退出时(无论该块如何退出:通过异常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++ 标准。

缺陷报告 应用于 发布时的行为 正确的行为
CWG 482 C++98 重声明的声明符不能限定 允许限定声明符
CWG 569 C++98 一个单独的分号不是有效的声明 它是一个空声明,
没有效果
CWG 1830 C++98 允许在decl-specifier-seq中重复函数说明符 禁止重复

[编辑] 参见

C 文档 关于 声明