std::identity{}需以花括号初始化作投影参数传入std::ranges::sort,实现零开销恒等映射,统一接口并支持泛型默认值,不可省略{}否则编译失败或重载错误。

std::identity 在 std::ranges::sort 中怎么用?
它不是用来“手动调用”的工具,而是作为投影参数传给算法,让算法对每个元素先做一次恒等映射——听起来没意义,但关键在于统一接口。比如你有一组 std::pair<int std::string></int>,想按 second 排序,但又希望保留将来切换成 first 或其他字段的灵活性,这时显式传 std::identity{} 就能占位:
常见错误现象:直接写 std::ranges::sort(v, std::identity) 报错,因为 std::identity 是类模板,不是可调用对象字面量
- 必须写成
std::identity{}(带花括号初始化),C++20 要求如此 - 若容器是
std::vector<:string></:string>,std::ranges::sort(v, std::identity{})等价于不传投影,但语义更明确:你确实在用投影机制 - 和 lambda
[](auto&& x) { return x; }行为一致,但std::identity{}零开销、无模板膨胀、支持 SFINAE 友好
为什么不用 auto&& x -> x 这种 lambda?
因为 lambda 会触发模板实例化爆炸,尤其在头文件中被多个 TU 包含时;而 std::identity 是标准库提供的单一类型,编译器能更好内联且符号更轻。
使用场景:当你写一个泛型函数,接受任意投影,就必须提供默认值——std::identity{} 是唯一合理、标准、无副作用的默认投影
- 例如:
template<typename r typename proj="std::identity"> void process(R&& r, Proj proj = {})</typename> - 若用户传
std::mem_fn(&Person::age),你仍能用同一份代码处理;若不传,就走恒等路径 - 注意:不能写
Proj proj = std::identity{},因为std::identity{}类型无法匹配模板参数默认值推导,必须用= {}让编译器从参数类型反推
std::identity 和 std::ref/std::cref 混用会出什么问题? 不会崩溃,但行为可能违背直觉:投影函数返回的是引用类型时,算法内部可能意外绑定到临时对象。
常见错误现象:std::ranges::transform(v, out, std::ref(func)) 合法,但 std::ranges::transform(v, out, std::identity{}) 返回的是值(或转发引用),和 std::ref 语义不兼容
-
std::identity{}对左值返回T&,对右值返回T&&,完全遵循完美转发规则 - 如果你强行把
std::identity{}和std::ref(x)套在一起,比如std::identity{}(std::ref(x)),结果是int&(假设 x 是 int),不是std::reference_wrapper<int></int> - 所以别把它当“万能包装器”——它只做一件事:原样透传,不多也不少
MSVC / GCC / Clang 对 std::identity 的支持差异
C++20 标准要求所有符合标准的实现都提供 std::identity,但早期版本有坑。
性能影响:无。它被优化为零指令,汇编里完全消失
立即学习“C++免费学习笔记(深入)”;
- Clang 12+、GCC 10.2+、MSVC 19.28+(VS 2019 16.8)起完整支持
std::identity{} - 旧版 MSVC(如 16.7)虽有声明但定义缺失,链接时报
undefined reference to std::identity::operator() - 跨平台项目建议加 feature test:
#if __cpp_lib_ranges >= 201911L,而不是依赖编译器版本号










