Type
对象 、 引用 、 函数 (包括 函数模板特化 )以及 表达式 都具有名为 类型 的属性,该属性既限制了这些实体允许执行的操作,又为原本通用的比特序列提供了语义含义。
目录 |
类型分类
C++ 类型系统包含以下类型:
- 基础类型 (另见 std::is_fundamental ):
-
- 类型 void (另见 std::is_void );
|
(since C++11) |
-
- 算术类型(另见 std::is_arithmetic ):
-
- 整型(包括 cv限定版本 ,另见 std::is_integral ,整型的同义词为整数类型):
-
- 类型 bool ;
- 字符类型:
-
- 窄字符类型:
-
- 普通字符类型: char 、 signed char 、 unsigned char [1]
|
(since C++20) |
-
-
-
-
- 宽字符类型: char16_t , char32_t , (C++11 起) wchar_t ;
- 有符号整数类型:
-
- 标准有符号整数类型: signed char , short , int , long , long long ;
-
-
-
|
(since C++11) |
-
-
-
- 无符号整数类型:
-
- 标准无符号整数类型: unsigned char 、 unsigned short 、 unsigned 、 unsigned long 、 unsigned long long ;
-
-
|
(since C++11) |
-
-
- 浮点类型(另见 std::is_floating_point ):
-
- 标准浮点类型: float 、 double 、 long double 及其 cv限定版本 ;
-
| (C++23 起) |
- 复合类型(另见 std::is_compound ):
-
- 引用类型 (另见 std::is_reference ):
-
- 左值引用类型 (另见 std::is_lvalue_reference ):
-
- 指向对象类型的左值引用;
- 指向函数类型的左值引用;
|
(since C++11) |
-
- 指针类型 (另见 std::is_pointer ):
- 指向成员指针类型 (另见 std::is_member_pointer ):
-
- 指向数据成员指针 类型(另见 std::is_member_object_pointer );
- 指向成员函数指针 类型(另见 std::is_member_function_pointer );
- 数组类型 (另见 std::is_array );
- 函数类型 (另见 std::is_function );
- 枚举类型 (另见 std::is_enum );
-
- 无作用域枚举类型 ;
|
(自 C++11 起) |
-
- 类类型 :
-
- 非联合类型(另见 std::is_class );
- 联合类型 (另见 std::is_union )。
- ↑ signed char 和 unsigned char 是窄字符类型,但它们不是字符类型。换句话说,窄字符类型的集合不是字符类型集合的子集。
对于每个非引用和非函数的非cv限定类型,类型系统支持该类型的三种附加 cv限定版本 ( const 、 volatile 以及 const volatile )。
其他分类
一个 对象类型 (参见 std::is_object )是指(可能带有cv限定符的)非函数类型、非引用类型,且非(可能带有cv限定符的) void 的类型。
以下类型统称为 标量类型 (另见 std::is_scalar ):
| (自 C++11 起) |
- 这些类型的 cv 限定版本
以下类型统称为 隐式生存期类型 :
- 标量类型
- 隐式生命周期类类型
- 数组类型
- 这些类型的cv限定版本
|
以下类型统称为 可平凡复制类型 :
以下类型统称为 标准布局类型 :
|
(C++11 起) |
| 类型特性层次结构图 |
|---|
|
注意:SVG图像中的元素可点击,但您需要先在新的浏览器标签页中打开该图表 |
已弃用的分类
|
以下类型统称为 POD类型 (另见 std::is_pod ):
|
(C++20起弃用) |
|
以下类型统称为 平凡类型 (另见 std::is_trivial ):
|
(C++11起)
(C++26起弃用) |
程序定义类型
一个 程序定义特化 是指不属于C++ 标准库 且不由实现定义的 显式特化 或 偏特化 。
一个 程序定义类型 是以下类型之一:
|
(since C++11) |
- 程序定义特化的 实例化 。
类型命名
可以通过以下方式声明 名称 以引用类型:
- class 声明;
- union 声明;
- enum 声明;
- typedef 声明;
- type alias 声明。
在C++程序中经常需要引用没有名称的类型;这种语法被称为
类型标识
。命名类型
T
的类型标识语法,与声明
T
类型变量或函数的语法完全一致,只是省略了标识符。但存在以下区别:声明语法中的
声明说明符序列
被限制为
类型说明符序列
,且仅当类型标识出现在非模板类型别名声明的右侧时,才可以定义新类型。
int* p; // 声明一个指向 int 的指针 static_cast<int*>(p); // 类型标识为 "int*" int a[3]; // 声明一个包含 3 个 int 的数组 new int[3]; // 类型标识为 "int[3]"(称为 new-type-id) int (*(*x[2])())[3]; // 声明一个包含 2 个指向函数的指针的数组 // 这些函数返回指向包含 3 个 int 的数组的指针 new (int (*(*[2])())[3]); // 类型标识为 "int (*(*[2])())[3]" void f(int); // 声明一个接受 int 并返回 void 的函数 std::function<void(int)> x = f; // 类型模板参数是一个类型标识 "void(int)" std::function<auto(int) -> void> y = f; // 同上 std::vector<int> v; // 声明一个 int 类型的 vector sizeof(std::vector<int>); // 类型标识为 "std::vector<int>" struct { int x; } b; // 创建一个新类型并声明该类型的一个对象 b sizeof(struct { int x; }); // 错误:不能在 sizeof 表达式中定义新类型 using t = struct { int x; }; // 创建一个新类型并声明 t 作为该类型的别名 sizeof(static int); // 错误:存储类说明符不是类型说明符序列的一部分 std::function<inline void(int)> f; // 错误:函数说明符也不允许
声明语法中去掉名称的 声明符 部分被称为 抽象声明符 。
类型标识符可在以下情形中使用:
- 在 强制转换表达式 中指定目标类型;
-
作为
sizeof、alignof、alignas、new和typeid的参数; - 在 类型别名 声明的右侧;
- 作为 函数 声明的后置返回类型;
- 作为 模板类型参数 的默认实参;
- 作为 模板类型参数 的模板实参;
|
(C++17 前) |
类型标识符可在以下经过修改的情况下使用:
- 在 函数 的参数列表中(当参数名被省略时),类型标识使用 decl-specifier-seq 而非 type-specifier-seq (特别地,允许使用某些存储类说明符);
- 在 用户定义转换函数 的名称中,抽象声明符不能包含函数或数组运算符。
|
本节内容不完整
原因:8.2[dcl.ambig.res] 若其可被简明概括 |
|
本节内容不完整
原因:需提及并链接至 decltype 和 auto |
详细类型说明符
详细类型说明符可用于引用先前声明的类名(class、struct 或 union)或先前声明的枚举名,即使该名称 被非类型声明隐藏 。它们也可用于声明新的类名。
请参阅 详细类型说明符 了解详情。
静态类型
通过程序编译时分析得到的表达式类型称为该表达式的 静态类型 。静态类型在程序执行期间不会改变。
动态类型
如果某个 泛左值表达式 引用的是 多态对象 ,其最终派生对象的类型被称为动态类型。
// 已知条件 struct B { virtual ~B() {} }; // 多态类型 struct D : B {}; // 多态类型 D d; // 最终派生对象 B* ptr = &d; // (*ptr) 的静态类型是 B // (*ptr) 的动态类型是 D
对于纯右值表达式,动态类型始终与静态类型保持一致。
不完整类型
以下类型属于 不完整类型 :
- 类型 void (可能带有 cv限定符 );
- 不完整定义的对象类型 :
所有其他类型都是完整的。
以下任意上下文均要求类型
T
为完整类型:
-
返回类型或参数类型为
T的函数的 定义 或调用; -
类型为
T的对象的 定义 ; -
类型为
T的 非静态类数据成员 的声明; -
类型为
T的对象或其元素类型为T的数组的new表达式 ; -
应用于类型为
T的泛左值的 左值到右值转换 ; -
到类型
T的 隐式 或 显式 转换; -
到类型
T
*
或
T
&
的
标准转换
、
dynamic_cast或static_cast,但从 空指针常量 或从 指向可能带 cv 限定符的 void 的指针 转换时除外; -
应用于类型为
T的表达式的 类成员访问运算符 ; -
应用于类型
T的typeid、sizeof或alignof运算符; -
应用于指向
T的指针的 算术运算符 ; -
基类为
T的类的定义; -
对类型为
T的左值的赋值; -
类型为
T、 T & 或 T * 的 异常处理程序 。
(通常,当需要知道
T
的大小和布局时。)
如果在翻译单元中出现以下任一情况,该类型的定义必须出现在同一翻译单元中。否则,则不作要求。
不完整定义的对象类型可以被补全:
- 类类型(例如 class X )在翻译单元的某个时间点可能被视为不完整类型,而在稍后时间点被视为完整类型;类型 class X 在这两个时间点始终是同一类型:
struct X; // X的声明,尚未提供定义 extern X* xp; // xp是指向不完整类型的指针: // 此时X的定义不可访问 void foo() { xp++; // 错误:X是不完整类型 } struct X { int i; }; // X的定义 X x; // 正确:X的定义已可访问 void bar() { xp = &x; // 正确:类型为“指向X的指针” xp++; // 正确:X已是完整类型 }
- 数组对象的声明类型可能是一个不完整类类型的数组,因此是不完整的;如果该类型在翻译单元后续被完整定义,则该数组类型变为完整类型;这两个时间点的数组类型是同一类型。
-
数组对象的声明类型可能是一个未知边界的数组,因此在翻译单元的某个时间点是不完整的,而在后续变为完整;这两个时间点的数组类型("未知边界的
T数组"和" N 个T的数组")是不同的类型。
指向未知边界数组的指针或引用类型永久指向或引用不完整类型。通过
typedef
声明命名的未知边界数组永久引用不完整类型。在这两种情况下,该数组类型均无法被补全:
extern int arr[]; // arr的类型是不完整的 typedef int UNKA[]; // UNKA是不完整类型 UNKA* arrp; // arrp是指向不完整类型的指针 UNKA** arrpp; void foo() { arrp++; // 错误:UNKA是不完整类型 arrpp++; // 正确:UNKA*的大小已知 } int arr[10]; // 现在arr的类型是完整的 void bar() { arrp = &arr; // 正确:限定转换(自C++20起) arrp++; // 错误:UNKA无法被补全 }
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 适用标准 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 328 | C++98 | 若从未创建该类的对象,则未禁止不完整类型的类成员 |
非静态类数据成员
必须为完整类型 |
| CWG 977 | C++98 |
枚举类型在其定义中何时变为
完整类型的规定不明确 |
确定底层类型后
该类型即为完整类型 |
| CWG 1362 | C++98 |
用户定义转换到
T*
或
T&
类型要求
T
为完整类型
|
无需此要求 |
| CWG 2006 | C++98 | cv限定 void 类型属于对象类型和完整类型 | 从这两个类别中排除 |
| CWG 2448 | C++98 | 仅限cv非限定类型可作为整型和浮点型 | 允许cv限定类型 |
| CWG 2630 | C++98 |
在类定义出现的翻译单元之外
该类是否视为完整类型不明确 |
若该情况下类定义
可达则视为完整类型 |
| CWG 2643 | C++98 |
指向未知边界数组的指针类型
无法被补全(但其本身已是完整类型) |
被指向的数组类型
不可补全 |
| LWG 2139 | C++98 | “用户定义类型”的含义不明确 |
定义并使用“程序定义类型”
作为替代 |
| LWG 3119 | C++11 | 闭包类型是否属于程序定义类型不明确 | 已明确说明 |
参考文献
- C++23 标准 (ISO/IEC 14882:2024):
-
- 6.8.2 基础类型 [basic.fundamental]
- C++20 标准 (ISO/IEC 14882:2020):
-
- 6.8.2 基础类型 [basic.fundamental]
- C++17 标准 (ISO/IEC 14882:2017):
-
- 6.9.1 基础类型 [basic.fundamental]
- C++14 标准 (ISO/IEC 14882:2014):
-
- 3.9.1 基本类型 [basic.fundamental]
- C++11 标准 (ISO/IEC 14882:2011):
-
- 3.9.1 基本类型 [basic.fundamental]
- C++98 标准 (ISO/IEC 14882:1998):
-
- 3.9.1 基本类型 [basic.fundamental]
参见
| 类型特性 | 基于模板的编译时接口,用于查询类型的属性 |
|
C 文档
关于
类型
|
|
外部链接
| 1. | Howard Hinnant的C++0x类型层次结构图 |