首页 文章

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

提问于
浏览
67

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

4 回答

  • 21

    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甚至's avoided partly), we don' t改进了语义;我们可以引入悬空指针,因为参考包装器不会延长指针的生命周期 .

    图书馆互动

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

    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 -functor时不需要复制该参数(或仿函数!)但保持在范围内,则非常有用 .

    与普通指针的区别

    • 没有额外级别的语法间接 . 必须取消引用指针才能获得它们所指对象的左值; reference_wrapper 具有隐式conversion operator,可以像它们包装的对象一样被调用 .
    int i;
    int& ref = std::ref(i); // Okay
    

    与指针不同,

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

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

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

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

    • std::reference_wrapper<T>std::make_tuple() 一起使用时,相应的元组元素变为 T& 而不是 T
    T object;
    f(std::make_tuple(1, std::ref(object)));
    
  • 16

    就自我记录代码而言,另一个区别是使用 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
    
  • 68

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

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

    它基本上是 CopyAssignableCopyAssignable 版本 . 任何时候你想要一个引用,但它必须是可分配的,使用 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
    

    比较:

    int i = 42;
    assert(std::ref(i) == std::ref(i)); // ok
    
    std::string s = "hello";
    assert(std::ref(s) == std::ref(s)); // compile error
    

相关问题