C++标准库中的std::identity是一个定义在<functional>头文件中的函数对象类,它的核心逻辑是接收一个参数并直接返回该参数,常被用作ranges算法中的默认投影选项。在ranges算法体系中,投影参数允许开发者指定一个转换函数,让算法先对元素执行转换再进行处理,而std::identity作为占位符,代表不对元素做任何转换。

std::identity的基础定义与特性
std::identity的实现非常简单,它是一个可调用对象,调用时返回传入的参数本身。它的存在主要是为了统一ranges算法的接口设计,让所有支持投影的算法都有一个默认的投影选项,避免接口设计上的冗余。
我们可以通过一个简单的示例来验证它的行为:
#include <functional>
#include <iostream>
int main() {
std::identity id;
int num = 10;
// 调用std::identity对象,返回传入的参数
auto result = id(num);
std::cout << result << std::endl; // 输出10
// 也可以直接通过临时对象调用
std::cout << std::identity{}(20) << std::endl; // 输出20
return 0;
}
ranges算法的投影功能是什么
ranges算法是C++20引入的新算法体系,相比传统算法,它支持直接操作范围对象,同时新增了投影参数。投影参数的作用是:算法在处理每个元素之前,先对元素应用投影函数,用投影后的结果参与算法的核心逻辑。
比如我们要对一个存储自定义结构体的范围做排序,希望按照结构体中的某个成员变量排序,传统做法需要写自定义比较器,而使用ranges算法的投影功能可以直接指定取成员变量的操作作为投影,简化代码。
std::identity在ranges算法中的使用演示
std::identity作为默认投影,在不需要对元素做转换的场景下使用,让代码逻辑更清晰。下面通过几个常见ranges算法的示例来演示它的作用。
示例1:std::ranges::sort中的投影
假设我们有一个整数数组,需要直接对数组元素排序,这时候可以使用std::identity作为投影,代表直接处理元素本身:
#include <algorithm>
#include <vector>
#include <iostream>
#include <functional>
int main() {
std::vector<int> nums = {3, 1, 4, 1, 5, 9};
// 使用std::identity作为投影,排序时直接处理元素本身
std::ranges::sort(nums, std::identity{});
for (int n : nums) {
std::cout << n << " "; // 输出1 1 3 4 5 9
}
std::cout << std::endl;
return 0;
}
如果这里不指定投影,ranges算法默认也会使用std::identity,显式写出来可以让代码的意图更明确,尤其是在后续可能修改投影逻辑的场景下,修改成本更低。
示例2:自定义结构体场景下的投影对比
我们定义一个学生结构体,分别演示使用自定义投影和std::identity投影的区别:
#include <algorithm>
#include <vector>
#include <iostream>
#include <functional>
#include <string>
struct Student {
std::string name;
int score;
};
int main() {
std::vector<Student> students = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78}
};
// 使用自定义投影,按照score成员排序
std::ranges::sort(students, {}, &Student::score);
std::cout << "按分数排序后:" << std::endl;
for (auto& s : students) {
std::cout << s.name << " " << s.score << std::endl;
}
// 如果我们要按照学生对象本身排序,需要Student支持比较操作,这里用std::identity作为投影
// 假设我们给Student添加了比较运算符,比较分数
// 这里仅为演示std::identity的使用场景
std::ranges::sort(students, std::identity{});
return 0;
}
std::identity的其他使用场景
除了作为ranges算法的默认投影,std::identity还可以在需要统一函数接口的场景下使用。比如某个函数模板接收一个转换函数作为参数,当调用方不需要转换时,可以传入std::identity,避免额外定义无用的函数对象。
下面是一个简单的函数模板示例:
#include <functional>
#include <iostream>
#include <vector>
// 模板函数,接收一个范围和一个转换函数,返回转换后的结果
template<typename Range, typename Transform>
auto process_range(const Range& r, Transform trans) {
std::vector<decltype(trans(*r.begin()))> result;
for (auto& elem : r) {
result.push_back(trans(elem));
}
return result;
}
int main() {
std::vector<int> nums = {1, 2, 3, 4};
// 不需要转换,传入std::identity
auto res1 = process_range(nums, std::identity{});
// 需要转换,传入lambda
auto res2 = process_range(nums, [](int x) { return x * 2; });
for (int n : res1) std::cout << n << " "; // 输出1 2 3 4
std::cout << std::endl;
for (int n : res2) std::cout << n << " "; // 输出2 4 6 8
std::cout << std::endl;
return 0;
}
总结
std::identity是一个简单的返回自身输入的函数对象,在C++的ranges算法体系中主要作为默认投影占位符使用,代表不对元素做任何转换。它的存在统一了ranges算法的接口设计,让支持投影的算法都有一致的默认行为。同时它也可以在其他需要默认转换函数的场景下使用,简化代码逻辑。理解它的作用有助于更好地使用C++20及后续标准中的ranges相关功能,写出更简洁易维护的代码。
std::identityC++_ranges投影功能占位符函数修改时间:2026-07-02 19:45:47