在C++标准库里,容器通常要求元素类型是对象类型,能够拷贝构造和赋值。但很多时候我们并不想拷贝大对象,只是想在容器里管理一批已有对象的“别名”。原生引用做不到这一点,而reference_wrapper提供了一个既像引用、又像对象的桥接类型,让我们可以在vector、list、map等容器中安全地存放引用。

为什么原生引用不能放进容器
原生引用在C++中不是对象,它没有独立的存储,也不能被重新绑定。以vector为例,当执行扩容、插入或排序操作时,容器需要移动或拷贝元素,这就要求元素类型至少是可拷贝赋值的。而引用一旦绑定就无法更改目标,也没有默认的空状态,因此下面的代码无法通过编译:
#include <vector> int a = 1, b = 2; std::vector<int&> ref_vec; // 错误:vector 的元素不能是引用类型 ref_vec.push_back(a); ref_vec.push_back(b);
除了语法限制,原生引用还会让容器失去很多通用算法的支持。标准库算法往往假设元素可以被赋值和交换,引用类型无法满足这些前提。因此如果确实需要在容器中保存“指向已有变量的把手”,就必须借助wrapper类。
reference_wrapper的基本用法
reference_wrapper定义在<functional>头文件中,模板参数为被引用的类型。它可以通过std::ref或std::cref创建,也可以直接构造。对象本身占用很小,内部通常只保存一个指针,因此放进容器几乎没有额外开销。
#include <iostream>
#include <vector>
#include <functional>
int main() {
int x = 10, y = 20, z = 30;
std::vector<std::reference_wrapper<int>> refs;
refs.push_back(std::ref(x));
refs.push_back(std::ref(y));
refs.push_back(z); // 也可以直接传对象,会隐式构造
for (int& v : refs) {
v += 1; // 修改会通过引用影响原变量
}
std::cout << x << " " << y << " " << z << std::endl; // 输出 11 21 31
return 0;
}
需要注意,reference_wrapper重载了operator T&,所以在大多数需要原引用类型的语境里能自动转换。例如上面用int&遍历时,不需要手动调用get()。如果确实需要显式取回引用,可以使用get()成员函数,这在模板推导不明确时很有用。
在关联容器和算法中的使用
reference_wrapper同样可以放在map或set中,但要注意它默认的相等和比较行为依赖于被引用对象的比较,而不是指针地址。如果存放的是reference_wrapper<int>,那么map会以int的值为键进行排序和去重。
#include <map>
#include <functional>
#include <iostream>
int main() {
int a = 5, b = 3, c = 5;
std::map<std::reference_wrapper<int>, int> m;
m[a] = 1;
m[b] = 2;
m[c] = 3; // c的值和a相同,会覆盖a的条目
for (auto& kv : m) {
std::cout << kv.first.get() << ":" << kv.second << std::endl;
}
return 0;
}
在排序、查找等算法中,reference_wrapper也能无缝参与,因为它可以隐式转为引用。不过如果容器中存的是reference_wrapper<const int>,则只能读取不能修改,这和普通const引用语义一致。使用std::cref可以方便地创建常量版本的wrapper。
生命周期与常见陷阱
reference_wrapper不拥有被引用对象的生命周期。如果原对象被销毁,而容器仍然保留wrapper,随后访问就会产生悬空引用,行为和普通引用失效一样危险。下面的例子展示了错误用法:
#include <vector>
#include <functional>
std::vector<std::reference_wrapper<int>> make_bad() {
int local = 42;
std::vector<std::reference_wrapper<int>> v;
v.push_back(local); // local 在函数返回后销毁
return v; // 返回的容器里全是悬空引用
}
要避免这类问题,应保证被引用的对象生命周期长于容器,或者容器本身只是短期使用。另一个常见误区是以为reference_wrapper能重新绑定,其实它和引用一样,构造时绑定,之后无法改变目标,只能拷贝出新的wrapper指向别处。
与指针和智能指针的对比
有人会问,为什么不直接用指针?指针可以为空、可以重新指向,灵活性更高,但也更容易出现空指针和所有权不清的问题。reference_wrapper强制在构造时绑定有效对象,语义上更明确:这里就是别名,不是可选资源。
| 方式 | 可否为空 | 可否重绑 | 语义 |
|---|---|---|---|
| 原生引用 | 否 | 否 | 别名,不能进容器 |
| reference_wrapper | 否 | 否 | 可进容器的别名 |
| 裸指针 | 是 | 是 | 可能为空的间接访问 |
| shared_ptr | 是 | 是 | 共享所有权 |
如果确实需要可选性或共享所有权,指针和智能指针更合适;如果只是临时组织一批已有对象做批量处理,reference_wrapper更轻量且表达意图清晰。结合标准算法如std::sort、std::for_each,它能写出既高效又易读的容器引用管理代码。
C++reference_wrapper容器存储引用修改时间:2026-08-04 10:57:31