当您第一次学习 C++ 时,您可能知道参考。它允许您像传递值一样传递参数,同时具有与传递指针类似的效果。但是,有时这可能无法按您的预期工作。在这篇文章中,我将讨论在正常方式不起作用时如何传递引用。

将参数传递给std::threadstd::bind

如果您使用过std::threador 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

cppreference

线程函数的参数按值移动或复制。

这是怎么做到的?答案是使用std::decay. 再次来自cppreference

对类型 T 应用左值到右值、数组到指针和函数到指针的隐式转换,删除 cv 限定符,并将结果类型定义为成员 typedef 类型。

简单来说,std::decay可以从类型中移除引用。例如,std::decay<int&>::type与 just 相同int。对于std::threadand 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::threador之类的引用std::bindstd::ref我希望您现在对 C++ 中的用途有了更好的理解。

参考

发表回复