type_traits 是编译期类型查询工具,非运行时机制、宏或反射;它提供模板类/变量(如std::is_integral),通过sfinae或constexpr if实现条件分支,仅回答类型是否满足特定属性,不支持成员遍历或动态自省。

type_traits 是什么,不是什么
std::is_integral、std::enable_if、std::remove_reference 这些不是运行时工具,也不是宏或代码生成器。它们是编译期常量表达式 + 模板元编程的组合体,本质是一组模板变量和模板类,靠 SFINAE 或 C++17 的 constexpr if 触发分支选择。
常见误解是把它当“反射”用——C++ 没有运行时类型信息(RTTI)意义上的自省,type_traits 只回答“这个类型满足某个条件吗”,比如是否为 const、是否可默认构造、是否能隐式转为 int。它不告诉你成员名、尺寸细节(除非你显式查 sizeof)、也不支持遍历字段。
怎么用 std::is_same 和 std::is_base_of 做安全分派
这两个最常用于模板约束和重载决议,而不是写 if 判断。直接在函数签名里用,比在函数体内写 if constexpr 更早拦截错误。
-
std::is_same_v<t int></t>返回bool字面量,在 C++17 起推荐用_v后缀变体,避免写::value -
std::is_base_of_v<base derived>对私有继承也返回true,但若Derived不完整(如前向声明后未定义),行为未定义——务必确保类型已完全定义 - 别在非模板上下文中用它们做运行时判断:比如
if (std::is_same_v<int t>)</int>在普通函数里会编译失败,因为T未定义
为什么 std::enable_if 容易 SFINAE 失败
SFINAE 不是异常机制,它只在模板参数推导或重载决议阶段起作用。一旦进入函数体,std::enable_if 就没用了。
立即学习“C++免费学习笔记(深入)”;
- 典型错误:把
typename = std::enable_if_t<...></...>放在函数参数列表末尾,但没给默认值,导致调用时必须传入第 2 个参数,反而破坏了重载意图 - 正确姿势是作为模板参数默认值:
template<typename t typename="std::enable_if_t<std::is_pointer_v<T">>></typename> - C++20 起优先用约束语法:
template<:integral t></:integral>或template<typename t> requires std::is_pointer_v<t></t></typename>,更清晰且错误信息友好 - 注意
std::enable_if_t在条件为false时是未定义类型,触发 SFINAE;但若写成std::enable_if<...>::type</...>,在 C++11 中可能因缺少type导致硬错误(hard error)而非 SFINAE
std::declval 的唯一合法用途就是 sizeof 和 decltype 表达式
std::declval<t>()</t> 不分配内存、不调用构造函数,只是告诉编译器“假设这里有个 T 类型的右值”。它不能出现在任何求值语境中,包括 return、if、for 等。
- 典型场景:检查某类型是否有某个成员函数:
decltype(std::declval<t>().foo())</t>,配合void_t或 C++20 concept 实现探测 - 错误用法:
T x = std::declval<t>();</t>编译失败,因为declval返回的是纯右值引用,且无定义实现 - 别试图用它绕过构造限制:比如对没有默认构造函数的类型调用
std::declval<nondefaultconstructible>()</nondefaultconstructible>是允许的,但仅限于不求值的上下文
真正难的是写一个可靠的 trait 来探测可调用性——涉及引用折叠、cv 限定符、重载集解析,稍不注意就会漏掉 const 成员函数或完美转发场景。这些边界情况,往往要靠大量 static_assert 和特化来兜底。









