C++ std::ref 学习笔记

1|0std::reference_wrapper


std::reference_wrapper 是 C++ 标准库中一个特殊的类模板,它的核心目的是将引用包装成一个可以复制、可以赋值的对象。这种设计解决了 C++ 中普通引用(T&)的一些局限性,尤其是在需要传递或存储引用语义的场景中。

1|1为什么需要 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& 是(通过隐式转换)
是否为对象 否(是别名) 是(是对象)

1|2std::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&

1|3典型使用场景


将引用存储在容器中

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(); }

1|4触发隐式类型转换的条件


  1. 传递给接受 T& 的函数参数

    void g(Example& ex) { ex.show(); } int main() { Example example; auto ref_ex = std::ref(example); g(ref_ex); // 正确:隐式转换为 Example& }
  2. 赋值给 T& 类型的变量

    Example example; auto ref_ex = std::ref(example); Example& ex_ref = ref_ex; // 隐式转换为 Example& ex_ref.show(); // 正确
  3. 在需要T& 的模板类型推导中

    template <typename T> void h(T& val) { // T 推导为 Example val.show(); } int main() { Example example; h(std::ref(example)); // 正确:T 推导为 Example,val 是 Example& }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18733020.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示