std::identity{} 必须带花括号,因它是类模板而非可调用对象;c++20 强制显式构造实例以满足 std::regular_invocable 约束,且仅能作为第三个参数(projection)传入 std::ranges::sort 等算法。

std::identity{} 必须带花括号,不写就编译失败
直接写 std::identity 是错的——它是个类模板,不是可调用对象。C++20 要求显式构造实例,所以必须用 std::identity{}(空花括号初始化)。漏掉 {} 会导致模板参数推导失败,常见报错如:no matching function for call to 'sort' 或 candidate template ignored: constraints not satisfied。
这不是风格问题,是标准强制要求:只有 std::identity{} 才满足 std::regular_invocable 约束,也才能被 std::ranges 算法识别为合法投影。
- ✅ 正确:
std::ranges::sort(v, std::identity{}) - ❌ 错误:
std::ranges::sort(v, std::identity)、std::ranges::sort(v, std::identity()) - ⚠️ 注意:
std::identity{}不是函数调用,不带括号的圆括号也不行
std::identity{} 只能传给 projection 参数位,放错位置就重载失败
std::ranges::sort 的参数顺序是 (range, comp, proj),其中 proj 是第三个参数。很多人把 std::identity{} 当成“比较器”塞进第二个参数位,结果触发 indirect_strict_weak_order 概念检查失败——因为 std::identity{} 根本不满足比较器要求。
正确做法是让 comp 使用默认值(即省略或传 std::ranges::less{}),再把 std::identity{} 明确作为第三个参数:
- ✅
std::ranges::sort(v, std::identity{})——comp默认,proj显式 - ✅
std::ranges::sort(v, std::ranges::less{}, std::identity{})—— 全显式,最清晰 - ❌
std::ranges::sort(v, std::identity{}, std::ranges::less{})—— 顺序颠倒,std::identity{}被当 comp 推导,失败
lambda 替代 std::identity{} 看似能跑,但会悄悄引入问题
写 [](auto&& x) { return x; } 在多数简单场景下确实能编译通过,但它和 std::identity{} 有本质区别:
- 编译开销:lambda 每次出现在不同翻译单元都会实例化新类型,头文件中滥用易导致模板膨胀;
std::identity{}是单一标准类型,符号轻、内联友好 - constexpr 支持:lambda 需显式加
constexpr且受限于捕获,而std::identity{}天然支持 constexpr 构造与调用 - concept 兼容性:在 requires 子句里约束
std::regular_invocable<proj t></proj>时,lambda 类型常因 SFINAE 不通过;std::identity{}是标准定义的“合规投影” - 语义明确性:lambda 可能隐含副作用(哪怕没写),而
std::identity{}是标准库保证的零副作用恒等映射
std::identity{} 和 std::ref / std::cref 混用要小心返回类型
std::identity{} 完美转发:对左值返回 T&,对右值返回 T&&。但如果你把它和 std::ref(x) 一起用,比如 std::identity{}(std::ref(x)),结果是 int&(假设 x 是 int),不是你期望的 std::reference_wrapper<int></int>。
这意味着它不会保留 std::reference_wrapper 的包装语义。若算法内部依赖引用包装做延迟绑定或跨生命周期访问,这种混用会导致静默行为变化:
- ✅ 安全场景:对普通容器元素做恒等投影,如
std::vector<:string></:string>排序 - ⚠️ 危险场景:投影返回引用后又被算法 move、存储或传递给非引用接收者,尤其在自定义迭代器或 proxy 类型中
- ? 提示:需要包装语义时,别用
std::identity{},改用显式 lambda 或专用 wrapper
真正容易被忽略的是:它不是语法糖,而是 ranges 投影机制的基石类型——一旦你在泛型函数里提供 Proj = std::identity{} 作为默认模板参数,就必须确保所有调用点都遵守花括号规则和参数位置,否则错误会分散在多个调用处,而非集中暴露。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











