std :: reference_wrapper和简单指针之间的区别?

时间:2014-11-05 20:55:39

标签: c++ pointers c++11 reference reference-wrapper

为什么需要std::reference_wrapper?应该在哪里使用?它与简单的指针有什么不同?它的性能如何与简单指针相比?

4 个答案:

答案 0 :(得分:76)

std::reference_wrapper与模板结合使用非常有用。它通过存储指向它的指针来包装对象,允许重新分配和复制,同时模仿其通常的语义。它还指示某些库模板存储引用而不是对象。

考虑STL中复制仿函数的算法:你可以通过简单地传递引用包装器而不是仿函数本身来避免该副本:

unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state

这是因为......

  • ... reference_wrapper s overload operator()因此可以像调用它们的函数对象一样调用它们:

    std::ref(myEngine)() // Valid expression, modifies myEngines state
    
  • ...(un)与普通引用一样,复制(和赋值)reference_wrappers只是指定了指针。

    int i, j;
    auto r = std::ref(i); // r refers to i
    r = std::ref(j); // Okay; r refers to j
    r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>
    

复制引用包装器实际上相当于复制指针,这种指针虽然便宜。使用它时固有的所有函数调用(例如operator()的函数调用)应该只是内联的。

reference_wrapper是通过std::ref and std::cref

创建的
int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>

template参数指定所引用对象的类型和cv限定条件; r2引用const int,只会引用const int。调用包含const仿函数的包装器只会调用const成员函数operator()

Rvalue初始化程序是不允许的,因为允许它们弊大于利。由于rvalues无论如何都会被移动(并且guaranteed copy elision即使部分避免了),我们也不会改进语义;我们可以引入悬空指针,因为参考包装器不会延长指针的生命周期。

图书馆互动

如前所述,可以通过make_tuple传递相应的参数,指示tuple在结果reference_wrapper中存储引用:

int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
                                        // Type of t2 is tuple<int&>

请注意,这与forward_as_tuple略有不同:此处,不允许使用rvalues作为参数。

std::bind显示相同的行为:它不会复制参数,但如果它是reference_wrapper则存储引用。如果在使用bind - 仿函数时不需要复制该参数(或仿函数!)但保持在范围内,则非常有用。

与普通指针的区别

  • 没有额外的语法间接层次。指针必须被解除引用才能获得它们所指对象的左值; reference_wrapper具有隐式conversion operator,可以像它们包装的对象一样被调用。

    int i;
    int& ref = std::ref(i); // Okay
    
  • reference_wrapper s,与指针不同,不具有空状态。必须使用either a reference or another reference_wrapper初始化它们。

    std::reference_wrapper<int> r; // Invalid
    
  • 相似度是浅拷贝语义:指针和reference_wrapper可以重新分配。

答案 1 :(得分:22)

std::reference_wrapper<T>至少有两个激励目的:

  1. 它是为作为值参数传递给函数模板的对象提供引用语义。例如,您可能有一个要传递给std::for_each()的大型函数对象,它按值获取其函数对象参数。为避免复制对象,可以使用

    std::for_each(begin, end, std::ref(fun));
    

    将参数作为std::reference_wrapper<T>传递给std::bind()表达式,通过引用而不是按值绑定参数是很常见的。

  2. std::reference_wrapper<T>std::make_tuple()一起使用时,相应的元组元素将变为T&而不是T

    T object;
    f(std::make_tuple(1, std::ref(object)));
    

答案 2 :(得分:18)

就自我记录代码而言,另一个区别是使用reference_wrapper基本上否定了对象的所有权。相比之下,unique_ptr断言所有权,而裸指针可能拥有或不拥有(如果不查看大量相关代码,则无法知道):

vector<int*> a;                    // the int values might or might not be owned
vector<unique_ptr<int>> b;         // the int values are definitely owned
vector<reference_wrapper<int>> c;  // the int values are definitely not owned

答案 3 :(得分:16)

您可以将其视为引用的便利包装器,以便您可以在容器中使用它们。

std::vector<std::reference_wrapper<T>> vec; // OK - does what you want
std::vector<T&> vec2; // Nope! Will not compile

它基本上是CopyAssignable的{​​{1}}版本。只要您想要一个引用,但它必须是可分配的,请使用T&或其辅助函数std::reference_wrapper<T>。或者使用指针。


其他怪癖:std::ref()

sizeof

比较:

sizeof(std::reference_wrapper<T>) == sizeof(T*) // so 8 on a 64-bit box
sizeof(T&) == sizeof(T) // so, e.g., sizeof(vector<int>&) == 24