std::ref 通过 std::reference_wrapper 包装左值,使模板函数能间接操作原始变量而非副本;它隐式转换为 T&,但类型推导为 wrapper 而非引用,需用 std::is_reference_wrapper_v 判断,且不延长对象生命周期。

std::ref 是怎么把值变成引用传进模板的
模板函数默认按值传递,哪怕你传的是变量,std::function、std::thread 或泛型算法内部也会拷贝一份。想让模板“看到”原始变量本身(比如后续要改它的值),就得用 std::ref 包一层——它不复制对象,只存一个指向原对象的引用。
常见错误现象:std::thread 里修改变量没生效;std::bind 捕获后原变量不变;容器里存 std::ref 后遍历时发现值没更新。
-
std::ref返回的是std::reference_wrapper类型,能隐式转成T&,所以能塞进大部分接受引用的地方 - 不能对临时变量用
std::ref(比如std::ref(42)),会编译失败:引用包装器必须绑定到左值 - 传给模板时,类型推导会变成
std::reference_wrapper而不是int&,如果模板内部做了decltype或std::is_reference判断,结果可能和预期不符
示例:
int x = 10;
auto f = std::bind([](int& v) { v *= 2; }, std::ref(x));
f(); // x 变成 20
std::cref 和 const 引用的绑定关系
std::cref 就是 std::ref 的 const 版本,生成的是 std::reference_wrapper。它保证被包装的对象在调用处不可修改,但不阻止原变量在别处被改——只是加了一层只读视图。
立即学习“C++免费学习笔记(深入)”;
使用场景:往只读回调、std::function 或需要 const 正确性的模板里传引用,又不想拷贝大对象(比如 std::string 或自定义结构体)。
- 如果你传了
const int& x,直接用std::cref(x);传非 const 变量但想强制只读,也用std::cref - 和
std::ref一样,不能绑定临时量:std::cref(3.14)错误 - 注意兼容性:
std::cref对象可以赋给std::ref变量(会触发隐式转换),但反过来不行——类型系统会拦住
模板里怎么判断参数是不是 std::reference_wrapper
当你写通用模板(比如日志包装器、转发函数),得区分“用户传的是真引用”还是“用户传了 std::ref 包装器”,否则解引用行为不一致。
关键点:C++17 起可以用 std::is_reference_v 判断是否为引用类型,但它对 std::reference_wrapper 返回 false;真正可靠的是 std::is_same_v<:decay_t>, std::reference_wrapper<:remove_reference_t>>>> ——太长,实践中更常用特化或 if constexpr 配合 std::is_reference_wrapper_v。
- C++17+ 直接用
std::is_reference_wrapper_v,返回true表示是包装器类型 - 拿到
std::reference_wrapper后,用.get()方法取回原始引用,别直接解引用(*wr也可以,但.get()更清晰) - 别在模板里对
std::reference_wrapper做std::move:它本身是轻量级对象,move 没意义,还可能误导读者以为要转移底层对象
std::ref 在 vector 或 tuple 里存引用的实际效果
容器不能直接存引用,但可以存 std::reference_wrapper。这是唯一安全、标准的方式让 std::vector “持有”外部变量的引用。
性能影响几乎为零:std::reference_wrapper 就是一个指针大小(通常 8 字节),无额外分配,无构造/析构开销。
-
std::vector<:reference_wrapper>>存的是指针语义,访问时用v[i].get()或隐式转int& - 容易踩的坑:如果被引用的变量生命周期短于容器,容器里的 wrapper 就悬空了——编译器不报错,运行时 UB
-
std::tuple里混用std::ref和值类型没问题,但提取时要注意:std::get(t)返回的是std::reference_wrapper,不是原始引用,需要.get()或 auto&& 绑定
结尾说一句:最常被忽略的是生命周期管理——std::ref 不延长对象生命,它只是个引用的“便携式标签”。用之前,先盯住那个变量到底活多久。









