std::identity 是 C++20 引入的标准化函数对象,用于原样转发参数;其核心价值在于类型精确性、SFINAE 友好性、完美转发语义及与 ranges 算法/视图的深度集成。

std::identity 是什么,为什么值得专门用它
std::identity 是 C++20 引入的函数对象,定义在 头文件中,作用就是“原样返回参数”:
struct identity { template constexpr T&& operator()(T&& t) const noexcept { return static_cast(t); } };
它不是语法糖,也不是为了省几行 lambda;它的核心价值在于**类型精确性**和**SFINAE 友好性**。比如 [](auto&& x) { return x; } 是泛型 lambda,会推导出具体闭包类型,而 std::identity{} 是标准库定义的可复制、可默认构造、有明确标准特化的类型,能通过 std::is_invocable_v、std::is_copy_constructible_v 等 trait 检查,也能被 std::ranges::transform 等算法正确识别为“无操作映射”。
在 std::ranges::transform 中替代空 lambda 更安全
当你只想把一个 range 的元素“原样搬过去”,比如转换容器但不改值:std::vector —— 这里如果只是想转类型,std::identity{} 并不合适;但如果你真要“完全透传”,比如 std::ranges::transform(v, w.begin(), std::identity{});,这时它比 [](auto&& x) { return x; } 更可靠:
-
std::identity{}保持引用类别(左值进,左值出;右值进,右值出),避免意外拷贝 - 在 ADL 或重载解析中不会干扰其他函数调用(lambda 会引入新类型)
- 能被
std::ranges::views::transform延迟求值管道直接接纳,且编译器更容易内联
配合 std::bind_front 或 std::ranges::sort 实现字段提取
std::identity 最实用的场景之一是“提取成员”或“透传字段”,尤其搭配 std::bind_front:
- 想按结构体某个字段排序?不用写
[](const auto& s) { return s.name; },直接std::ranges::sort(vec, {}, std::bind_front(&Person::name, std::identity{})); - 这里
std::identity{}作为占位符,让std::bind_front把后续传入的对象(比如Person&)自动绑定到&Person::name的第一个参数上 - 同理可用于
std::ranges::max_element、std::ranges::accumulate的投影参数(projection)
std::identity 不是万能的,这些地方别硬套
- 它不能代替带逻辑的转换:比如需要
std::abs、std::tolower 或自定义归一化,必须用对应函数或 lambda
- 它不处理 const/volatile 限定:若源 range 是
const std::vector,std::identity{} 返回的仍是 const int&,但如果你期望非 const 引用,得显式 cast 或改用其他策略
- 在模板元编程中,
std::identity 类型本身常被用作“占位模板参数”,例如 template struct wrapper; ,此时它代表“默认不做变换”,但要注意:C++20 前没有 std::identity,这类代码需 feature-test 或 fallback
std::abs、std::tolower 或自定义归一化,必须用对应函数或 lambda const std::vector,std::identity{} 返回的仍是 const int&,但如果你期望非 const 引用,得显式 cast 或改用其他策略 std::identity 类型本身常被用作“占位模板参数”,例如 template struct wrapper; ,此时它代表“默认不做变换”,但要注意:C++20 前没有 std::identity,这类代码需 feature-test 或 fallback 真正容易被忽略的是:它在管道式表达中带来的语义清晰度——当你看到 | std::views::transform(std::identity{}),你知道这步纯粹是为了类型适配或占位,而不是藏着隐式转换逻辑。这种“显式无操作”恰恰是现代 C++ 元编程和 range 库设计的关键习惯。
立即学习“C++免费学习笔记(深入)”;










