std::identity是c++20引入的函数对象,定义在中,其operator()直接返回参数本身,用于明确表达非修改式投影——即原样透传元素,不作任何变换,满足ranges算法对恒等投影的语义、constexpr及concept约束要求。

std::identity 是什么,它在投影中起什么作用
std::identity 是 C++20 引入的函数对象,定义在 <functional></functional> 中,其调用操作符 operator() 直接返回传入参数本身。它不是“占位符”或“空操作”,而是明确表达“不修改、不转换、仅透传”的语义——这正是非修改式投影(non-mutating projection)的核心需求。
在算法如 std::sort、std::max_element 或 std::ranges::sort 中,projection 参数用于提取比较依据(比如取结构体的某个成员)。当你要按整个对象本身比较(而非某字段),又不想写 [](const auto& x) { return x; } 这种冗余 lambda,std::identity{} 就是标准、零开销、可读性高的选择。
什么时候必须用 std::identity 而不是直接省略 projection
省略 projection 通常可行,但某些场景下显式使用 std::identity{} 是必要或更安全的:
- 当你调用的是接受 projection 的重载(如
std::ranges::sort),且容器元素类型不支持默认比较(比如自定义类没定义operator),但你想按对象内存布局或地址顺序排——这时传 <code>std::identity{}明确告诉算法“用对象本身作 key”,避免误推导成其他重载 - 在泛型代码中,projection 类型需统一:比如模板函数接受
Proj模板参数,用户可能传std::identity{}或&T::id;若你内部逻辑依赖 projection 可调用,用std::identity{}可保证接口一致,不用特化分支处理“无 projection”情况 - 某些算法(如
std::ranges::unique)要求 projection 返回可比较类型;若元素类型不可比但你希望按地址去重,std::identity{}配合自定义比较器(如std::less)才能生效,省略 projection 则编译失败
std::identity 在 std::ranges 算法中的典型用法
std::ranges 算法普遍支持 projection,std::identity{} 在这里最自然。注意:必须显式构造(std::identity{}),不能只写 std::identity(后者是类名,非可调用对象)。
示例:对 vector<:string> 按字符串长度排序,但保留原字符串(非修改式):
std::vector<:string> v = {"hello", "hi", "world"};
std::ranges::sort(v, {}, std::identity{}); // 第二参数是 comp,默认 std::less;第三是 projection</:string>
等价于:std::ranges::sort(v, std::less{}, std::identity{})。这里 std::identity{} 把每个 std::string& 投影为自身,然后 std::less{} 比较两个 std::string 对象。
常见错误:
- 写成
std::identity(缺少{})→ 编译错误:类型不可调用 - 在需要 const-qualified 投影的上下文中(如
const std::vector<t>&</t>),std::identity{}默认支持 const 调用,无需额外修饰 - 误以为
std::identity{}会触发拷贝:它只是转发引用,返回的是const T&或T&&,和手动写 lambda 行为一致
和手写 lambda 相比,std::identity 有什么实际差异
语义和性能上几乎无差别,但有三点关键区别:
-
std::identity{}是字面量类型(literal type),可在 constexpr 上下文中使用;而[](auto&& x) { return x; }在 C++20 前不是字面量,C++20 起虽支持但需捕获为空且无 mutable,不如std::identity直观可靠 - 模板推导更干净:比如
template<class proj> void f(Proj p)</class>,传std::identity{}推导为std::identity类型;传 lambda 则推导为未命名闭包类型,可能干扰 SFINAE 或概念约束 - 调试时更易识别:gdb/LLDB 中看到
std::identity::operator()比看到一串匿名 lambda 地址更利于定位问题
真正容易被忽略的是:std::identity 的模板参数是隐式推导的(内部用 auto&&),它完美转发 cv 和引用限定,但如果你手动写 lambda 并用了 const T&,就可能丢失右值引用支持——这点在移动语义敏感的投影中(比如投影返回临时对象)会暴露出来。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











