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 3std::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 1std::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 推导为 Exampleval.show(); }int main() {Example example;h(std::ref(example)); // 正确:T 推导为 Example,val 是 Example& }