std::reference_wrapper 是 C++ 标准库 <functional> 头文件中的一个类模板,它的主要作用是将引用包装成一个可拷贝、可赋值的对象

1. 什么是 std::reference_wrapper

在 C++ 中,引用(reference)本身不能被重新赋值,也不能作为容器的元素,因为它们不符合大多数标准库容器和算法对**可拷贝(Copyable)可赋值(Assignable)**的要求。

std::reference_wrapper<T> 解决了这个问题。它内部持有一个 T 类型的引用,但它本身是一个对象,可以像普通对象一样被拷贝和赋值。当你拷贝一个 std::reference_wrapper 对象时,你拷贝的是对同一个原始对象的引用,而不是拷贝原始对象本身。

2. 为什么需要它?

std::reference_wrapper 的主要用途是让引用能够与标准库容器和算法协同工作

许多标准库组件,比如 std::vectorstd::liststd::threadstd::sort 等,要求其操作的对象是可拷贝的。如果你直接尝试将引用放入这些容器或传递给这些函数,编译器会报错。

例如,下面的代码无法编译,因为 std::vector 不允许存储引用:

1
2
3
4
// 错误示例:不能直接存储引用
#include <vector>
int x = 10;
// std::vector<int&> my_vec; // 编译错误!

通过使用 std::reference_wrapper,你可以绕过这个限制,将引用“伪装”成一个可存储在容器中的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <vector>
#include <functional> // 必须包含此头文件

int main() {
int a = 10;
int b = 20;

// std::vector<std::reference_wrapper<int>> 允许存储对 int 对象的引用
std::vector<std::reference_wrapper<int>> my_vec;

my_vec.push_back(std::ref(a)); // std::ref 是一个辅助函数,用于创建 std::reference_wrapper
my_vec.push_back(std::ref(b));

// 修改容器中的第一个元素,实际上是修改了原始变量 a
my_vec[0].get() = 100; // 使用 .get() 来获取被包装的引用

std::cout << "Original value of a: " << a << std::endl; // 输出: Original value of a: 100
std::cout << "Original value of b: " << b << std::endl; // 输出: Original value of b: 20

return 0;
}

std::refstd::cref 辅助函数:

为了方便使用,C++ 标准库提供了两个辅助函数:

  • std::ref(T& obj):创建一个 std::reference_wrapper<T> 对象。

  • std::cref(const T& obj):创建一个 std::reference_wrapper<const T> 对象,用于常量引用。

3. std::reference_wrapper 的常见用途

  • 容器存储引用: 如上例所示,将引用放入 std::vectorstd::map 等容器中。

  • 多线程传递可变对象: 当使用 std::thread 启动新线程时,如果你希望线程函数能够修改一个外部变量,你需要通过 std::ref 传递引用。直接传递变量会发生拷贝。

  • 泛型编程与算法: 在使用如 std::bindstd::sort 等算法时,如果你想让它们操作原始对象而不是拷贝,std::reference_wrapper 是一个重要的工具。

例子:std::sort 的应用

假设你有一个自定义对象的向量,你想根据其中一个成员变量对它们进行排序,但你不想拷贝整个对象。你可以创建一个存储 std::reference_wrapper 的向量来对原始对象进行间接排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

class MyObject {
public:
int id;
std::string name;
MyObject(int i, const std::string& n) : id(i), name(n) {}
};

int main() {
std::vector<MyObject> objs;
objs.emplace_back(2, "B");
objs.emplace_back(1, "A");
objs.emplace_back(3, "C");

// 创建一个包含对原始对象引用的向量
std::vector<std::reference_wrapper<MyObject>> refs;
for (auto& obj : objs) {
refs.push_back(std::ref(obj));
}

// 使用 sort 对引用进行排序,比较器通过 .get() 访问原始对象
std::sort(refs.begin(), refs.end(), [](const std::reference_wrapper<MyObject>& a, const std::reference_wrapper<MyObject>& b) {
return a.get().id < b.get().id;
});

// 原始向量 objs 保持不变,但 refs 向量已经排序
std::cout << "Sorted references:" << std::endl;
for (const auto& ref : refs) {
std::cout << "ID: " << ref.get().id << ", Name: " << ref.get().name << std::endl;
}

return 0;
}

总结:

std::reference_wrapper 是一种“引用对象”,它解决了 C++ 中引用不能被拷贝、不能被赋值、不能作为容器元素的问题。它的核心价值在于让引用能够无缝地融入 C++ 标准库的容器和算法生态系统,从而在需要传递引用而非拷贝时提供了强大的支持。