类型
对象、引用、函数(包括函数模板特化)和表达式都具有一个称为**类型**的属性,该属性既限制了这些实体允许的操作,又为原本通用的位序列提供了语义含义。
目录 |
[编辑] 类型分类
C++ 类型系统包含以下类型
- 基本类型(另请参阅 std::is_fundamental)
- 类型 void(另请参阅 std::is_void);
|
(C++11 起) |
- 算术类型(另请参阅 std::is_arithmetic)
- 整型类型(包括cv 限定版本,另请参阅 std::is_integral,整型类型的同义词是整数类型)
- 类型 bool;
- 字符类型
- 窄字符类型
- 普通字符类型:char、signed char、unsigned char[1]
|
(C++20 起) |
- 宽字符类型:char16_t、char32_t,(C++11 起)wchar_t;
- 有符号整型类型
- 标准有符号整型类型:signed char、short、int、long、long long;
|
(C++11 起) |
- 无符号整型类型
- 标准无符号整型类型:unsigned char、unsigned short、unsigned、unsigned long、unsigned long long;
|
(C++11 起) |
- 浮点类型(另请参阅 std::is_floating_point)
- 标准浮点类型:float、double、long double 及其cv 限定版本;
(C++23 起) |
- 复合类型(另请参阅 std::is_compound)
- 引用类型(另请参阅 std::is_reference)
-
- 到对象类型的左值引用;
- 到函数类型的左值引用;
|
(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++ 标准库,也不是由实现定义的。
一个*程序定义类型*是以下类型之一
|
(C++11 起) |
- 一个程序定义的特化的实例化。
[编辑] 类型命名
可以通过以下方式声明一个名称来引用一个类型:
在 C++ 程序中,那些没有名称的类型常常需要被引用;其语法被称为 type-id。命名类型 T
的 type-id 语法与声明类型 T
的变量或函数的声明语法完全相同,只是省略了标识符,并且声明语法的 decl-specifier-seq 被限制为 type-specifier-seq,而且只有当 type-id 出现在非模板类型别名声明的右侧时才能定义新类型。
int* p; // declaration of a pointer to int static_cast<int*>(p); // type-id is "int*" int a[3]; // declaration of an array of 3 int new int[3]; // type-id is "int[3]" (called new-type-id) int (*(*x[2])())[3]; // declaration of an array of 2 pointers to functions // returning pointer to array of 3 int new (int (*(*[2])())[3]); // type-id is "int (*(*[2])())[3]" void f(int); // declaration of a function taking int and returning void std::function<void(int)> x = f; // type template parameter is a type-id "void(int)" std::function<auto(int) -> void> y = f; // same std::vector<int> v; // declaration of a vector of int sizeof(std::vector<int>); // type-id is "std::vector<int>" struct { int x; } b; // creates a new type and declares an object b of that type sizeof(struct { int x; }); // error: cannot define new types in a sizeof expression using t = struct { int x; }; // creates a new type and declares t as an alias of that type sizeof(static int); // error: storage class specifiers not part of type-specifier-seq std::function<inline void(int)> f; // error: neither are function specifiers
声明语法中移除了名称的 declarator 部分被称为 abstract-declarator。
Type-id 可用于以下情况
- 在转换表达式中指定目标类型;
- 作为
sizeof
、alignof
、alignas
、new
和typeid
的参数; - 在类型别名声明的右侧;
- 作为函数声明的尾随返回类型;
- 作为模板类型参数的默认参数;
- 作为模板类型参数的模板参数;
|
(C++17 前) |
Type-id 可以在以下情况下进行一些修改后使用
- 在函数的参数列表中(当省略参数名称时),type-id 使用 decl-specifier-seq 而不是 type-specifier-seq(特别是,允许某些存储类说明符);
- 在用户定义转换函数的名称中,抽象声明符不能包含函数或数组运算符。
本节不完整 原因:8.2[dcl.ambig.res] 如果可以简洁地总结 |
本节不完整 原因:提及并链接到 decltype 和 auto |
[编辑] 详细类型说明符
详细类型说明符可用于引用先前声明的类名(class、struct 或 union)或先前声明的枚举名,即使该名称被非类型声明隐藏。它们也可用于声明新的类名。
有关详细信息,请参阅详细类型说明符。
[编辑] 静态类型
通过程序编译时分析得到的表达式的类型称为表达式的*静态类型*。静态类型在程序执行期间不会改变。
[编辑] 动态类型
如果某个glvalue 表达式引用了多态对象,则其最派生对象的类型称为动态类型。
// given struct B { virtual ~B() {} }; // polymorphic type struct D : B {}; // polymorphic type D d; // most-derived object B* ptr = &d; // the static type of (*ptr) is B // the dynamic type of (*ptr) is D
对于 prvalue 表达式,动态类型总是与静态类型相同。
[编辑] 不完整类型
以下类型是*不完整类型*
所有其他类型都是完整类型。
以下任何上下文都要求类型 T
是完整类型
- 定义或调用返回类型为
T
或参数类型为T
的函数; - 定义类型为
T
的对象; - 声明类型为
T
的非静态类数据成员; - 为类型
T
的对象或元素类型为T
的数组的new
表达式; - 应用于类型
T
的 glvalue 的左值到右值转换; - 到类型
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; // declaration of X, no definition provided yet extern X* xp; // xp is a pointer to an incomplete type: // the definition of X is not reachable void foo() { xp++; // ill-formed: X is incomplete } struct X { int i; }; // definition of X X x; // OK: the definition of X is reachable void bar() { xp = &x; // OK: type is “pointer to X” xp++; // OK: X is complete }
- 数组对象的声明类型可能是包含不完整类类型的数组,因此是不完整的;如果该类类型随后在翻译单元中完成,则数组类型变为完整类型;这两个点的数组类型是同一类型。
- 数组对象的声明类型可能是未知边界的数组,因此在翻译单元中的某一点是不完整的,之后又变为完整的;这两个点的数组类型(“未知边界的
T
数组”和“N 个T
数组”)是不同类型。
指向或引用未知边界数组的指针或引用类型永久指向或引用不完整类型。由 typedef
声明命名的未知边界数组永久引用不完整类型。在这两种情况下,数组类型都不能完成
extern int arr[]; // the type of arr is incomplete typedef int UNKA[]; // UNKA is an incomplete type UNKA* arrp; // arrp is a pointer to an incomplete type UNKA** arrpp; void foo() { arrp++; // error: UNKA is an incomplete type arrpp++; // OK: sizeof UNKA* is known } int arr[10]; // now the type of arr is complete void bar() { arrp = &arr; // OK: qualification conversion (since C++20) arrp++; // error: UNKA cannot be completed }
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 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 文档 用于 Type
|
[编辑] 外部链接
1. | Howard Hinnant 的 C++0x 类型树 |