C++ std::ref 学习笔记
std::reference_wrapper
std::reference_wrapper 是 C++ 标准库中一个特殊的类模板,它的核心目的是将引用包装成一个可以复制、可以赋值的对象。这种设计解决了 C++ 中普通引用(T&)的一些局限性,尤其是在需要传递或存储引用语义的场景中。
为什么需要 std::reference_wrapper?
在 C++ 中,普通引用(T&)有以下限制:
- 不能直接存储在容器中:例如
std::vector<T&>是无效的。 - 不能直接复制或赋值:引用本身不是对象,只是一个别名。
- 模板参数推导的局限性:当模板函数需要按值传递参数时,引用语义会丢失。
std::reference_wrapper<T> 通过将引用包装成一个对象,解决了这些问题:
- 可以存储在容器中:例如
std::vector<std::reference_wrapper<T>>是合法的。 - 可以复制和赋值:它本身是一个对象,满足可复制构造和可复制赋值。
- 隐式转换为原始引用:在使用时,可以无缝转换为
T&,保持引用语义。
| 特性 | 普通引用 (T&) |
std::reference_wrapper<T> |
|---|---|---|
| 是否可复制 | 否 | 是 |
| 是否可存储到容器 | 否 | 是 |
是否隐式转换到 T& |
本身就是 T& |
是(通过隐式转换) |
| 是否为对象 | 否(是别名) | 是(是对象) |
std::reference_wrapper 的核心特性
创建
std::reference_wrapper可由std::ref,std::cref创建
int a = 123;
auto ref1 = std::ref(a); // reference_wrapper<int>
auto ref2 = std::cref(a); // reference_wrapper<const int>
可复制、可赋值的对象
int a = 123;
int &ref1 = a;
int &ref2 = ref1; // ref2 仍然是 a 的引用,不是复制
auto ref3 = std::ref(a);
auto ref4 = ref3; // ref3, ref4 是两个不同的对象
隐式转换为原始引用
void print(int &x) { cout << x << "\n"; }
int main() {
int a = 123;
auto ref_a = std::ref(a);
print(ref_a); // 隐式转换为 int &
}
显式获取引
int a = 123;
auto ref_a = std::ref(a);
int& raw_ref = ref_a.get(); // 显式获取 int&
典型使用场景
将引用存储在容器中
int main() {
int a = 1, b = 2, c = 3;
cout << " " << a << " " << b << " " << c << "\n"; // 1 2 3
std::vector<std::reference_wrapper<int>> vec = {a, b, c};
vec[0].get() = 4;
cout << " " << a << " " << b << " " << c << "\n"; // 4 2 3
}
与STL算法结合
int main() {
int a = 2, b = 3, c = 1;
std::vector<std::reference_wrapper<int>> vec = {a, b, c};
for(auto i : vec) cout << i.get() << " \n"[i == vec.back()]; // 2 3 1
std::ranges::sort(vec);
for(auto i : vec) cout << i.get() << " \n"[i == vec.back()]; // 1 2 3
}
绑定到需要引用的函数
比如std::bind, std::thread这些函数默认是传值,如果你真的需要传引用可以用std::ref显示的传引用。
void foo(int &x) { cout << (++x) << " "; }
int main() {
int x{}, y{};
auto f = std::bind(foo, x);
auto g = std::bind(foo, std::ref(y));
f(), f(), f(), cout << x << "\n"; // 1 2 3 0
g(), g(), g(), cout << y << "\n"; // 1 2 3 3
}
这种用户也可以使得与第三方库(如Boost库)保持行为一致,确保代码可移植性。
与不可复制的对象交互
int main() {
std::unique_ptr<int> ptr(new int(123));
auto foo = [](std::unique_ptr<int> &p) { cout << *p << "\n"; };
foo(ptr); // 可以执行 p 直接引用 prt
// auto f = std::bind(foo, ptr);
// 错误写法, 因为std::bind 要调用拷贝构造,但是unique_prt不能拷贝只能复制
auto f = std::bind(foo, std::ref(ptr));
f();
}
触发隐式类型转换的条件
-
传递给接受
T&的函数参数void g(Example& ex) { ex.show(); } int main() { Example example; auto ref_ex = std::ref(example); g(ref_ex); // 正确:隐式转换为 Example& } -
赋值给
T&类型的变量Example example; auto ref_ex = std::ref(example); Example& ex_ref = ref_ex; // 隐式转换为 Example& ex_ref.show(); // 正确 -
在需要
T&的模板类型推导中template <typename T> void h(T& val) { // T 推导为 Example val.show(); } int main() { Example example; h(std::ref(example)); // 正确:T 推导为 Example,val 是 Example& }

浙公网安备 33010602011771号