类型萃取

类型萃取
苏丙榅1. 类型萃取
1.1 核心概念
类型萃取这是 C++11 引入的特性(并在 C++14/17/20 中得到了极大的扩展),是进行 模板元编程 和编写通用泛型代码的基础工具。它提供了一系列编译时的模板类和模板别名,用于在编译期间检查、修改和操作类型。这些功能主要集中 C++ 标准库的 <type_traits> 头文件中。
在 C++ 中文技术社区和文献中,<type_traits> 通常有以下几种常见的称呼,根据上下文略有不同:
官方翻译:类型萃取,意为从类型中提取信息。
在 C++ 标准库中文版文档以及许多经典书籍(如《C++ Primer》译本)中,通常使用这个词来描述这种在编译期获取类型特性的机制。
编程术语:类型特性
<type_traits>头文件提供的正是各种“类型”的“特性”(比如是否有 const 修饰符、是否是整数、是否是类等)。通俗称呼:类型工具 或 类型辅助
在日常开发交流中,程序员很少专门说出一个学术名词,通常会说用一下
type_traits或用类型工具判断一下。
类型萃取允许在编译期确定类型的某种属性。例如,判断一个类型是否是指针、是否是 const 修饰的、两个类型是否相同,或者获取类型的成员变量类型等。
在编写模板代码时,我们经常需要对不同的类型进行不同的处理。例如,一个排序函数模板,针对内置类型(如 int)可以使用快速拷贝(如 memcpy),而针对自定义类类型(如 Student)则必须调用拷贝构造函数。类型萃取帮助编译器做出这种选择。
大多数 <type_traits> 中的类都有一个共同的结构模式。它们通常是一个类模板,接受一个或多个类型参数,并包含以下成员:
value: 一个static constexpr成员变量。- 如果是 判断类 traits,它是
bool类型(如true或false)。 - 如果是 变换类 traits,它可能是整数或其他值(如
std::integral_constant)。
- 如果是 判断类 traits,它是
type: 一个公有的typedef或using定义的类型别名。- 如果是 判断类 traits,它通常是输入类型本身(主要用于继承)。
- 如果是 变换类 traits,它是变换后的新类型。
C++17 对 <type_traits> 做了一些增强,使得代码更加简洁易读。C++11/14 时期,我们写代码比较啰嗦:
1 |
|
C++17 引入了 _v 和 _t 后缀(其实 _t 是 C++14 引入的,但在 17 中已成为标配习惯):
1 |
|
2. 详细特性分类表 (C++17 版)
以下列出的所有 xxxx_v 变量模板均定义为 inline constexpr bool xxxx_v = xxxx::value;。
下面表格并未列出所有类,如有需要可自行查询类型萃取相关类 API 在线文档
2.1 基础类型判断
用于检查类型 T 是否具有某种基础性质。
| 特性类 | C++17 变量模板 | 描述 |
|---|---|---|
is_void<T> |
is_void_v<T> |
是否是 void |
is_null_pointer<T> |
is_null_pointer_v<T> |
是否是 std::nullptr_t |
is_integral<T> |
is_integral_v<T> |
是否为整型 (int, char, bool 等) |
is_floating_point<T> |
is_floating_point_v<T> |
是否为浮点型 (float, double) |
is_array<T> |
is_array_v<T> |
是否为数组 |
is_pointer<T> |
is_pointer_v<T> |
是否为指针 |
is_lvalue_reference<T> |
is_lvalue_reference_v<T> |
是否为左值引用 |
is_enum<T> |
is_enum_v<T> |
是否为枚举类型 |
is_union<T> |
is_union_v<T> |
是否为联合体 |
is_class<T> |
is_class_v<T> |
是否为类或结构体 |
is_function<T> |
is_function_v<T> |
是否为函数类型 |
is_member_pointer<T> |
is_member_pointer_v<T> |
是否为成员函数指针或成员变量指针 |
is_arithmetic<T> |
is_arithmetic_v<T> |
是否为算术类型: 整数或浮点数 |
is_fundamental<T> |
is_fundamental_v<T> |
是否为基本类型: void、整数、浮点、nullptr |
is_object<T> |
is_object_v<T> |
是否为对象类型 |
is_compound<T> |
is_compound_v<T> |
是否为复合类型 |
is_reference<T> |
is_reference_v<T> |
是否为引用:左值或右值引用 |
2.2 类型属性判断
检查类型的更深层属性。
| 特性类 | C++17 变量模板 | 描述 |
|---|---|---|
is_const<T> |
is_const_v<T> |
是否有 const 修饰 |
is_volatile<T> |
is_volatile_v<T> |
是否有 volatile 修饰 |
is_trivial<T> |
is_trivial_v<T> |
是否为平凡类型 (可安全 memcpy) |
is_standard_layout<T> |
is_standard_layout_v<T> |
是否为标准布局 (兼容 C 语言) |
is_pod<T> (C++20起移除) |
is_pod_v<T> |
是否为 POD 类型 (既是 trivial 又是 standard_layout) |
is_empty<T> |
is_empty_v<T> |
是否为空类 (无数据成员,虚函数通常不为空) |
is_polymorphic<T> |
is_polymorphic_v<T> |
是否为多态类 (含虚函数) |
is_abstract<T> |
is_abstract_v<T> |
是否为抽象类 (含纯虚函数) |
2.3 类型关系判断
检查类型之间的相互关系。
| 特性类 | C++17 变量模板 | 描述 |
|---|---|---|
is_same<T, U> |
is_same_v<T, U> |
T 和 U 是否完全相同 |
is_base_of<Base, Der> |
is_base_of_v<Base, Der> |
Base 是否是 Der 的基类 |
is_convertible<T, U> |
is_convertible_v<T, U> |
T 是否可隐式转换为 U |
is_invocable<...> |
is_invocable_v<...> |
[C++17] 给定参数是否可调用 |
invoke_result<F, Args> |
invoke_result_t<F, Args> |
推导调用结果的类型 |
在 C++17 之前,很难非常精准地判断一个对象是否可以被调用,以及调用的结果类型是什么。C++17 引入了一系列工具:
is_invocable<Callable, Args...>: 判断Callable是否可以用参数Args...调用。is_invocable_r<Result, Callable, Args...>: 判断是否可调用,且返回类型可转换为Result。is_invocable_r_v<R, Callable, Args...>不仅检查能不能调用,还要检查调用的返回值是否可以隐式转换为R。invoke_result_t<Callable, Args...>:(C++17) 推导调用结果的类型(替代已废弃的result_of( C++11 ))。
1 |
|
std::is_invocable 的第一个模板参数要求传入的是 Type(类型),而不是 Expression(表达式/变量)。
func是一个标识符(虽然它代表函数地址)。std::is_invocable< ... >尖括号里必须填类型。decltype(func)才是func的类型。
所以,必须要用某种方式把 func 变成类型,或者手动写出类型。
2.4 类型修改
注意:这些不是 _v 变量,而是 type 成员。C++14/17 使用 _t 别名模板。
| 特性类 | C++14/17 别名模板 | 描述 |
|---|---|---|
remove_const<T> |
remove_const_t<T> |
移除 const |
remove_volatile<T> |
remove_volatile_t<T> |
移除 volatile |
remove_cv<T> |
remove_cv_t<T> |
移除 const 和 volatile |
add_const<T> |
add_const_t<T> |
添加 const |
remove_reference<T> |
remove_reference_t<T> |
移除引用 |
add_lvalue_reference<T> |
add_lvalue_reference_t<T> |
添加左值引用 & |
add_rvalue_reference<T> |
add_rvalue_reference_t<T> |
添加右值引用 && |
remove_pointer<T> |
remove_pointer_t<T> |
移除顶层指针 |
add_pointer<T> |
add_pointer_t<T> |
添加指针 |
make_signed<T> |
make_signed_t<T> |
转为有符号类型 |
make_unsigned<T> |
make_unsigned_t<T> |
转为无符号类型 |
remove_extent<T> |
remove_extent_t<T> |
移除数组的一维 |
remove_all_extents<T> |
remove_all_extents_t<T> |
移除数组的所有维度 |
decay<T> |
decay_t<T> |
类型退化:数组转指针、函数转指针、去 cv、去引用 |
2.5 特性逻辑组合
C++11 时只能写 std::is_integral::value && std::is_floating_point::value。C++17 提供了类型层面的逻辑运算。
| 特性类 | 描述 |
|---|---|
conjunction<B1...> |
逻辑与。如果所有 Bi 都为 true,则为 true。短路求值(遇到 false 停止)。 |
disjunction<B1...> |
逻辑或。如果任意 Bi 为 true,则为 true。短路求值。 |
negation<B> |
逻辑非。取反。 |
定义一个类型特性,检查是否为数字类型。示例代码如下:
1 |
|













