C++ 标准库 type_traits
下面对 C++ 标准库中 <type_traits>
头文件提供的编译期类型检测与类型变换工具做一次系统、深入的梳理,包括类型属性查询(property queries)、类型关系判断(type relations)、类型变换(type transformations)、条件类型(conditional traits)、以及辅助变量模板,并配以典型示例与实践建议。
一、概述
<type_traits>
中定义了一系列编译期模板,用于对类型特性进行布尔查询(如是否为指针、是否可拷贝、是否为浮点型等),以及基于原始类型生成新类型(如添加/去除const
/volatile
、提取内置数组元素类型、替换void
为nullptr_t
等)。- 所有工具均在编译期计算,可用于 SFINAE、
static_assert
、模板元编程和泛型库设计,提升类型安全性与灵活性。
二、类型属性查询(Property Queries)
用于判断类型是否满足某些性质,结果以 std::true_type
或 std::false_type
表示,可通过其静态成员 value
或 C++17 以上的变量模板 .type
读取。
Trait | 功能 |
---|---|
std::is_void<T> | 是否为 void 类型 |
std::is_integral<T> | 是否为整型(包括 bool 、枚举) |
std::is_floating_point<T> | 是否为浮点型 |
std::is_array<T> | 是否为原生数组 |
std::is_pointer<T> | 是否为指针类型 |
std::is_lvalue_reference<T> | 是否为左值引用 |
std::is_rvalue_reference<T> | 是否为右值引用 |
std::is_member_object_pointer<T> | 是否为指向成员对象的指针 |
std::is_member_function_pointer<T> | 是否为指向成员函数的指针 |
std::is_enum<T> | 是否为枚举类型 |
std::is_union<T> | 是否为联合类型 |
std::is_class<T> | 是否为类类型 |
std::is_function<T> | 是否为函数类型 |
std::is_same<T,U> | 两类型是否完全相同 |
std::is_base_of<Base,Derived> | Base 是否为 Derived 的基类(含同类型) |
std::is_convertible<From,To> | From 是否可隐式转换为 To |
static_assert( std::is_integral<int>::value, "int must be integral" );
static_assert( std::is_pointer<double*>::value, "pointer check" );
三、类型关系判断(Type Relations)
Trait | 功能 |
---|---|
std::is_const<T> | 是否带 const |
std::is_volatile<T> | 是否带 volatile |
std::is_signed<T> / std::is_unsigned<T> | 整型是否带符号 / 无符号 |
std::is_trivial<T> | 是否为平凡类型(trivial default ctor + trivial copy + trivial dtor) |
std::is_trivially_copyable<T> | 是否可平凡拷贝 |
std::is_standard_layout<T> | 是否为标准布局类型(layout-compatible) |
std::is_pod<T> | 是否为 POD 类型(is_trivial && is_standard_layout ) |
std::is_literal_type<T> | 是否可用作字面常量类型(C++11,等同于 C++20 is_literal_type ) |
std::is_empty<T> | 是否为空类 |
std::is_polymorphic<T> | 是否多态(含虚函数) |
std::is_abstract<T> | 是否抽象类 |
std::is_final<T> (C++14) | 是否标记为 final |
struct A { int x; };
static_assert( std::is_trivially_copyable<A>::value, "A must be trivially copyable" );
四、类型变换(Type Transformations)
将一种类型“映射”到另一种类型,常用于参数处理与特化清洗。
Trait | 功能 |
---|---|
std::remove_const<T> | 去除顶层 const |
std::remove_volatile<T> | 去除顶层 volatile |
std::remove_cv<T> | 去除顶层 const volatile |
std::add_const<T> | 添加顶层 const |
std::add_volatile<T> | 添加顶层 volatile |
std::add_cv<T> | 添加顶层 const volatile |
std::remove_reference<T> | 去除引用(左值或右值) |
std::add_lvalue_reference<T> | 添加左值引用 |
std::add_rvalue_reference<T> | 添加右值引用 |
std::remove_pointer<T> | 去除指针 |
std::add_pointer<T> | 添加指针 |
std::remove_extent<T> | 去除数组第一级维度 |
std::remove_all_extents<T> | 去除所有数组维度 |
std::decay<T> | 模拟函数模板参数衰减,去引用、去 cv 并转数组、函数为指针 |
std::enable_if<B, T> | 若 B 为 true ,typedef 为 T ;否则无成员 |
std::conditional<B, T, F> | 若 B 为 true 得到 T ,否则 F |
std::common_type<Ts...> | 推导多类型的公共类型 |
std::underlying_type<Enum> | 枚举的底层整型类型 |
std::result_of<F(Args...)> (C++17 deprecated) / std::invoke_result<F,Args...> | 推导调用结果类型 |
using T1 = std::remove_const<const int>::type; // int
using T2 = std::decay<int[5]>::type; // int*
using T3 = std::conditional<true, float, double>::type; // float
五、辅助变量模板(Variable Templates,C++17 以上)
为查询型 trait 提供 _v
后缀的布尔常量,更简洁:
std::is_integral_v<T> // 等价于 std::is_integral<T>::value
std::is_same_v<T,U>
std::remove_const_t<T> // 等价于 typename std::remove_const<T>::type
std::decay_t<T>
六、典型应用示例
1. SFINAE 限制模板重载
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
foo(T x) { /* integral version */ }
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
foo(T x) { /* floating version */ }
2. 通用转发与完美转发
template<typename F, typename... Args>
auto invoke(F&& f, Args&&... args)
-> std::invoke_result_t<F, std::decay_t<Args>...>
{
return std::forward<F>(f)(std::forward<Args>(args)...);
}
3. 元编程构造器
template<typename... Ts>
struct Tuple {
using Common = std::common_type_t<Ts...>;
Common data[sizeof...(Ts)];
};
七、实践建议
- 优先使用
_v
、_t
- C++17 以上代码中,尽量用
std::is_const_v<T>
、std::remove_cv_t<T>
等更简洁。
- C++17 以上代码中,尽量用
enable_if
vs Concepts- C++20 有了 Concepts,可替代大量
enable_if
,提升可读性与诊断信息。
- C++20 有了 Concepts,可替代大量
- 避免过度元编程
- 仅在必要时(泛型库、框架)使用复杂类型推导;业务逻辑中尽量保持类型直观。
- 组合 trait
- 可借助
std::conjunction
、std::disjunction
、std::negation
(C++17) 构造复杂条件。
- 可借助
- 关注编译期效率
- 过度嵌套 trait 会影响编译时间;合理拆分或使用变量模板缓存结果。
通过以上对 <type_traits>
中类型属性查询、关系判断、变换操作与条件类型的全面梳理,以及变量模板与示例,相信能帮助你在模板元编程、泛型库设计和高效类型安全代码中得心应手地运用标准库工具。祝编码顺利!