std::identity是c++20引入的零开销恒等函数对象,定义在中,其operator()完美转发参数,专为满足ranges算法对投影(proj)的concept约束(如indirectly_unary_invocable)而设计,必须用std::identity{}显式初始化并作为投影参数传入,不可省略花括号或替换为lambda。

std::identity 是什么,为什么能当投影用
std::identity 是 C++20 引入的函数对象,定义在 <functional></functional> 中,它的作用就是原样返回传入的参数:operator()(T&& t) { return std::forward<t>(t); }</t>。它天然满足“投影”(projection)的要求:接受一个元素,返回一个可比较/可操作的值。所以只要算法支持 proj 参数(比如 std::ranges::sort、std::ranges::max_element),你就能用它替代手写 lambda 或冗余的 [] (auto&& x) { return x; }。
注意:它不是“默认”自动启用的——C++ 标准库中没有任何算法把 std::identity{} 设为 proj 的默认参数;你得显式传进去,否则就走无投影路径。
哪些算法支持 std::identity 作投影参数
支持 proj 的算法集中在 std::ranges 命名空间(C++20 起),常见有:
-
std::ranges::sort、std::ranges::stable_sort
-
std::ranges::find_if、std::ranges::count_if(但注意:这些对 proj 的语义是“先投影再判断”,不是所有场景都适用)
-
std::ranges::min_element、std::ranges::max_element
-
std::ranges::lower_bound、std::ranges::upper_bound(要求投影后结果可比较)
std::ranges::sort、std::ranges::stable_sort
std::ranges::find_if、std::ranges::count_if(但注意:这些对 proj 的语义是“先投影再判断”,不是所有场景都适用)std::ranges::min_element、std::ranges::max_element
std::ranges::lower_bound、std::ranges::upper_bound(要求投影后结果可比较)老式非 ranges 算法(如 std::sort)不接受 proj,强行传会编译失败。别混用。
std::identity 投影的典型用法与易错点
最常见场景是想对自定义类型按某个成员排序,但又不想写 lambda。例如:
struct Person {
std::string name;
int age;
};
std::vector<person> v = {{"Alice", 30}, {"Bob", 25}};
std::ranges::sort(v, {}, &Person::age); // 成员指针投影 —— 这不是 std::identity
std::ranges::sort(v, {}, std::identity{}); // 按 Person 对象本身排序(需定义 operator
<p>关键区别:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<ul>
<li>用 <code>std::identity{}</code> 表示“我就要用这个对象本身参与比较”,所以 <code>Person</code> 必须可比较(比如有 <code>operator 或提供 comparator)</code>
</li>
<li>如果想按 <code>name</code> 排序但又懒得写 <code>[] (const auto& p) { return p.name; }</code>,那应该用 <code>std::ranges::sort(v, {}, &Person::name)</code>,而不是 <code>std::identity</code>
</li>
<li>
<code>std::identity{}</code> 和不传 <code>proj</code> 并不等价:前者明确指定投影逻辑,后者可能触发不同重载(比如某些算法无 <code>proj</code> 版本只接受 comparator)</li>
</ul><h3>性能和兼容性要注意什么
</h3><p><code>std::identity</code> 是零开销抽象:编译器几乎总能内联,生成的汇编和手写 <code>return x;</code> 一样。但它依赖 C++20,若项目还在用 C++17,不能直接用 —— 此时可自己仿一个轻量版:</p>
<pre class="brush:php;toolbar:false;">struct identity {
template<class t>
constexpr T&& operator()(T&& t) const noexcept { return std::forward<t>(t); }
};</t></class>
不过要注意:标准 std::identity 支持 const 重载和 SFINAE 友好,自定义版若没处理好 const 成员访问或完美转发边界,可能在复杂模板上下文中出问题。
真正容易被忽略的是:当你把 std::identity{} 传给一个期望投影返回 const 类型的算法(比如某些需要 const T& 比较的自定义 range adaptor),编译器可能因 cv-qualifier 不匹配而报错,这时得检查投影返回值类型是否和 comparator 期望一致。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










