std::type_identity 是 C ++20 引入的类型惰性包装器,用于在模板参数推导中将指定类型设为非推导上下文,从而阻止编译器自动退化(如数组→指针、cv 限定符丢失等),确保传入类型原样保留。

std::type_identity 是干啥的?
它就是个“类型隐身衣”:把一个类型原封不动包一层,让模板参数推导时别瞎猜。你写 std::type_identity_t<int></int>,得到的还是 int,但编译器在推导模板函数参数时,会把它当非推导上下文——不参与类型推导,也就不会覆盖你传进去的原始类型。
典型场景:你写了个泛型函数,想让某个参数类型严格按你传的来,而不是被自动退化(比如数组变指针、cv 限定符丢掉、引用折叠搞乱)。这时候不套 std::type_identity,T 就可能被悄悄改写。
什么时候必须用它?常见推导翻车现场
下面这些情况,不用 std::type_identity,T 就不是你以为的 T:
- 传入数组:
foo(arr)中arr是int[5],但模板template<typename t> void foo(T)</typename>会把T推成int*,丢失长度和数组类型 - 传入带 const/volatile 的引用:比如
const int&,推导后可能变成int或int&,const消失 - 转发函数中想保留原始 cv/ref 属性:比如完美转发前先做类型检查,但检查逻辑又不能干扰推导
这时就得把那个“想锁死”的参数,用 std::type_identity_t<t></t> 包住:
立即学习 “C++ 免费学习笔记(深入)”;
template<typename T> void process(std::type_identity_t<T> x) {/* x 的类型就是你传进来的原样 */}
和 std::decay、std::remove_reference 有啥区别?
std::decay 和 std::remove_reference 是主动“改”类型:一个做退化(数组→指针、函数→指针、去掉引用 /const),一个只去引用。而 std::type_identity 什么也不改,只起“阻断推导”作用。
关键差异在用途:
-
std::decay_t<t></t>→ 你想标准化类型(比如存进容器或做统一处理) -
std::remove_reference_t<t></t>→ 你明确只想甩掉引用,其他不管 -
std::type_identity_t<t></t>→ 你不想让模板推导碰这个类型,但也不动它,原样透传
混用容易出错:比如写成 process(std::decay_t<t> x)</t>,那类型早被改过了,再“锁”也没意义。
实际写法注意点和坑
它只在模板参数声明位置起作用,用错地方等于没用:
- ✅ 正确:函数参数类型写成
std::type_identity_t<t></t>(推导时该参数不参与T推导) - ❌ 错误:写成
auto x = std::type_identity_t<t>{val}</t>—— 这里没推导发生,std::type_identity完全多余 - ❌ 错误:在返回类型里单独用,比如
-> std::type_identity_t<t></t>,不阻止参数推导,只是绕口令 - ⚠️ 注意:C++20 起才有,别在 C++17 项目里硬上;MSVC 19.28+、GCC 10+、Clang 11+ 支持良好
最常被忽略的一点:它只影响「该形参对应的那个模板参数」的推导。如果你有多个模板参数,得挨个包,不能指望一个 std::type_identity 全局生效。






























