c++oding="utf-8" ?>
std::identity{} 是 c++20 ranges 算法中唯一合法、零开销且满足 concept 约束的恒等投影对象,必须用花括号初始化,不可省略或替换为 lambda。

std::identity{} 是 C++20 ranges 算法中唯一合法、零开销、满足 concept 约束的恒等投影对象;它不是可选语法糖,而是当你需要「显式声明不作变换」时不可替代的语义占位符。
std::identity{} 必须带花括号初始化,否则编译失败
std::identity 是空结构体类型,仅支持聚合初始化。写成 std::identity 是类型名,std::identity() 会尝试调用不存在的默认构造函数,二者都会导致模板参数推导失败或重载解析错误。
-
std::ranges::sort(v, std::identity{})✅ 正确:构造空对象,满足indirectly_unary_invocableconcept -
std::ranges::sort(v, std::identity)❌ 错误:类型不匹配,SFINAE 失败 -
std::ranges::sort(v, std::identity())❌ 错误:“no matching constructor”
std::identity{} 在 ranges 算法中不能被 lambda 替代
虽然 [](auto&& x) { return x; } 行为上等价,但它无法满足标准库对投影的严格约束:
- 不保证
constexpr构造与调用(影响requires检查) - 每次实例化生成新类型,破坏编译缓存、增加模板膨胀
- 在 concept 约束如
std::regular_invocable<:identity t></:identity>中,lambda 类型不满足该要求 - 部分 libstdc++ 实现会对
std::identity{}做零开销内联优化,lambda 则无此保障
std::identity{} 必须放在正确参数位置,否则触发错误重载
ranges 算法的投影参数(Proj)是独立于比较器(Comp)的第三个(或更后)参数。误将其传入第二个位置,会导致 Comp 模板参数推导失败。
-
std::ranges::sort(v, std::ranges::less{}, std::identity{})✅ 投影在第三位 -
std::ranges::sort(v, std::identity{})✅ 省略比较器,默认less{},std::identity{}仍作为第三参数被接受 -
std::ranges::sort(v, std::identity{}, std::ranges::less{})❌ 编译失败:std::identity{}被当作Comp,不满足indirect_strict_weak_order - 类似地,
std::ranges::max_element(v, {}, std::identity{})✅ 投影是第四个参数,顺序不能错
std::identity{} 在泛型模板中是唯一安全的默认投影值
当你封装一个接受投影的函数模板时,Proj = std::identity{} 是唯一能同时满足「可省略」「可显式传入」「不破坏 concept 检查」三重要求的默认值。
-
template<typename r typename proj="std::identity"> void sort_by(R&& r, Proj proj = {});</typename>❌ 错误:默认模板参数是类型,= {}无法初始化类型名 -
template<typename r typename proj="std::identity"> void sort_by(R&& r, Proj proj = std::identity{})</typename>✅ 正确:提供实例,且std::identity{}可参与模板实参推导 - 若用
Proj = [](auto&& x){return x;},则每次调用生成新类型,模板无法复用 - 省略投影参数虽在简单调用中可行,但在 SFINAE 或 requires 子句中易引发推导歧义
最容易被忽略的一点是:std::identity{} 的语义不是“什么也不做”,而是“明确启用投影机制但不做变换”——这决定了它在算法重载选择、concept 约束、模板推导路径中的关键地位;漏掉花括号、放错位置、或用 lambda 替代,表面可能编译通过,实则已脱离标准行为边界。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











