Type traits 是编译期类型查询与转换工具,本质是通过模板偏特化、类型别名和 constexpr 常量实现逻辑判断(如是否为 const/整数)或类型变换(如去除引用),而非运行时获取类型名;decltype 和 typeid 分别返回带修饰的表达式类型和运行时字符串,无法参与模板特化或 static_assert。

什么是 type traits,为什么不能直接用 decltype 或 typeid
类型萃取不是“获取类型名”,而是编译期对类型做逻辑判断或转换——比如判断一个类型是否是 const、是否可拷贝、是否是整数类型,或者把 T& 变成 T。直接用 decltype 或 typeid 拿到的是运行时信息或带修饰的类型表达式,没法参与模板特化或 static_assert 断言。
真正起作用的是模板偏特化 + 类型别名 + constexpr 常量成员,靠编译器在实例化时匹配最精确的特化版本。
-
decltype(x)返回表达式的类型(含引用/const),但它是语法层面的结果,无法直接用于条件分支 -
typeid(T).name()是运行时字符串,不可用于模板参数或if constexpr - 标准库的
std::is_integral_v这类东西背后全是偏特化类模板,不是函数调用
手写一个 remove_reference:理解偏特化和别名模板
这是最基础也最容易出错的 type trait:把 T&、T&& 剥掉引用,得到 T。关键点在于必须覆盖所有引用情况,且不能漏掉 cv 限定符。
template<typename t>
struct remove_reference {
using type = T;
};
<p>template<typename t>
struct remove_reference<t> {
using type = T;
};</t></typename></p>
<p>template<typename t>
struct remove_reference<t> {
using type = T;
};</t></typename></p></typename>
注意:remove_reference 会匹配第二个偏特化,type 是 const int,不是 int——这是正确行为。如果想进一步去掉 const,得组合用 remove_const。
- 别名模板
template是必需的,否则每次都要写using remove_reference_t = typename remove_reference ::type; typename X::type - 不能只写
T&特化而漏掉T&&,否则对右值引用失效 - 偏特化顺序无关,但主模板必须存在(哪怕只是兜底)
实现 is_same:SFINAE 和 constexpr 的分界线
C++11 起,is_same 通常用偏特化实现;C++17 后更推荐用 constexpr 变量模板,更简洁且支持非类型模板参数比较。
template<typename t typename u>
struct is_same : std::false_type {};
<p>template<typename t>
struct is_same<t t> : std::true_type {};</t></typename></p></typename>
这是经典写法,依赖偏特化唯一匹配。但要注意:
-
is_same是::value false,因为int≠int&—— 类型严格相等,不考虑引用折叠 - 如果要用在
static_assert中,建议搭配is_same_v别名(C++17),避免反复写::value - 不要试图用
std::is_same在非模板上下文中做编译期分支——它只是常量,不是控制流::value
自己写 enable_if 时容易忽略的细节
std::enable_if 的核心是“让模板参数推导失败”,从而触发 SFINAE。手动实现时,重点不在逻辑判断,而在让 type 成员仅在条件为真时存在。
template<bool b typename t="void">
struct enable_if {};
<p>template<typename t>
struct enable_if<true t> {
using type = T;
};</true></typename></p></bool>
常见错误:
- 写成
template—— 缺少默认模板参数using enable_if_t = typename enable_if::type; T = void,会导致enable_if_t尝试访问不存在的::type,直接编译失败而非 SFINAE - 在函数模板中用
typename enable_if<...>::type* = nullptr作默认参数,而不是作为返回类型——后者会干扰重载解析 - 误以为
enable_if是合法的空类型,其实它根本没定义::type type,访问即硬错误
现代代码优先用 requires(C++20)替代 enable_if,但理解它的原理仍能帮你读懂大量旧代码和标准库实现。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











