在 C++ 中为什么需要 std::ref 传递引用
当您第一次学习 C++ 时,您可能知道参考。它允许您像传递值一样传递参数,同时具有与传递指针类似的效果。但是,有时这可能无法按您的预期工作。在这篇文章中,我将讨论在正常方式不起作用时如何传递引用。
将参数传递给std::thread
和std::bind
如果您使用过std::thread
or std::bind
,您可能会注意到,即使您将引用作为参数传递,它仍然会创建一个副本。
#include <functional>
#include <iostream>
using namespace std;
void inc(int& x)
{
x++;
cout << "Inside inc, x is now " << x << endl;
}
int main()
{
int a = 0;
auto func1 = bind(inc, a);
func1();
cout << "After func1, value of a is " << a << endl;
}
Inside inc, x is now 1
After func1, value of a is 0
线程函数的参数按值移动或复制。
这是怎么做到的?答案是使用std::decay
. 再次来自cppreference,
对类型 T 应用左值到右值、数组到指针和函数到指针的隐式转换,删除 cv 限定符,并将结果类型定义为成员 typedef 类型。
简单来说,std::decay
可以从类型中移除引用。例如,std::decay<int&>::type
与 just 相同int
。对于std::thread
and std::bind
,当它们存储您传入的参数时,std::decay<T>::type
将用作每个参数的类型。因此,当传入引用时,会创建一个副本,而不是简单地存储一个引用。
#include <iostream>
#include <type_traits>
using namespace std;
int main()
{
int a = 0;
int& a_ref = a;
decay<int&>::type a_copy = a_ref;
a_ref++;
cout << a << endl;
cout << a_copy << endl;
}
1
0
std::ref
救援
为了克服std::decay
对引用的影响,我们必须有一个在内部存储引用的可复制对象。这正是这样std::reference_wrapper
做的。
std::reference_wrapper 是一个类模板,它将引用包装在可复制、可分配的对象中。它经常被用作将引用存储在通常不能保存引用的标准容器(如 std::vector)中的一种机制。
std::ref
是标准库中用于创建std::reference_wrapper
对象的便捷函数。有了std::ref
,我们终于可以正确地将引用作为参数传递,如下所示(继续上面的示例)。
int main()
{
int a = 0;
int b = 0;
auto func1 = bind(inc, a);
func1();
cout << "After func1, value of a is " << a << endl;
auto func2 = bind(inc, ref(b));
func2();
cout << "After func2, value of b is " << b << endl;
}
Inside inc, x is now 1
After func1, value of a is 0
Inside inc, x is now 1
After func2, value of b is 1
结论
在这篇文章中,我描述了这个std::ref
函数,以及为什么我们需要使用它来传递对诸如std::thread
or之类的引用std::bind
。std::ref
我希望您现在对 C++ 中的用途有了更好的理解。