c++pointersc++11referencereference-wrapper

What is the difference between std::reference_wrapper and a simple pointer?


Why is there a need to have std::reference_wrapper? Where should it be used? How is it different from a simple pointer? How is it's performance compared to a simple pointer?


Solution

  • std::reference_wrapper is useful in combination with templates. It refers to an object by storing a pointer to it, allowing for reassignment and copy while mimicking reference (lvalue) semantics. It also instructs certain library templates to store references instead of objects.

    Consider the algorithms in the Standard Library which copy functors: You can avoid that copy by simply passing a reference wrapper referring to the functor instead of the functor itself:

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

    This works because…

    Copying a reference wrapper is practically equivalent to copying a pointer, which is as cheap as it gets. All the function calls inherent in using it (e.g. the ones to operator()) should be just inlined as they are one-liners.

    reference_wrappers are created via 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>
    

    The template argument specifies the type and cv-qualification of the object referred to; r2 refers to a const int and will only yield a reference to const int. Calls to reference wrappers with const functors in them will only call const member function operator()s.

    Rvalue initializers are disallowed, as permitting them would do more harm than good. Since rvalues would be moved anyway (and with guaranteed copy elision even that's avoided partly), we don't improve the semantics; we can introduce dangling pointers though, as a reference wrapper does not extend the pointee's lifetime.

    Library interaction

    As mentioned before, one can instruct make_tuple to store a reference in the resulting tuple by passing the corresponding argument through a 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&>
    

    Note that this slightly differs from forward_as_tuple: Here, rvalues as arguments are not allowed.

    std::bind shows the same behavior: It won't copy the argument but store a reference if it is a reference_wrapper. Useful if that argument (or the functor!) need not be copied but stays in scope while the bind-functor is used.

    Difference from ordinary pointers