Difference between std :: reference_wrapper and simple pointer?

Why is there a need to have std::reference_wrapper ? Where to use it? How does this differ from a simple pointer? How does its performance compare with a simple pointer?

+48
c ++ pointers reference c ++ 11
Nov 05 '14 at 20:55
source share
4 answers

std::reference_wrapper is useful in combination with templates. It wraps the object, keeping a pointer to it, allowing you to reassign and copy, simulating the usual semantics. He also instructs certain library templates to store links instead of objects.

Consider the algorithms in STL that functors copy: you can avoid this copy by simply passing a reference wrapper with a reference to the functor instead of the functor itself:

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

This works because ...

  • ... reference_wrapper overload operator() so that they can be called in the same way as the objects of the functions to which they refer:

     std::ref(myEngine)() // Valid expression, modifies myEngines state 
  • ... (un) like regular links, copying (and assigning) reference_wrappers just assigns a pointee.

     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> 

Copying a reference wrapper is almost equivalent to copying a pointer, which is just as cheap as it is. All function calls inherent in its use (for example, those specified in operator() ) should simply be inlined, as they are single-line.

reference_wrapper are created using 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 indicates the type and cv qualification of the referenced object; r2 refers to const int and gives only a reference to const int . Calls for reference wrappers with const functors in them will only call the const member function operator() s.

Rvalue initializers are not allowed, as this gives them more harm than good. Since rvalues ​​will move anyway (and with guaranteed copy permission , even if this is partially avoided), we do not improve the semantics; we can introduce dangling pointers, though, since the reference shell does not extend the life of the pointer.

Library Interaction

As mentioned earlier, you can tell make_tuple to save the link in the resulting tuple by passing the corresponding argument through 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 is slightly different from forward_as_tuple : Here rvalues ​​are not valid as arguments.

std::bind shows the same behavior: it does not copy the argument, but saves the link if it is reference_wrapper . Useful if this argument (or functor!) Does not need to be copied, but remains in scope while using bind functor.

Unlike regular pointers

  • There is no additional level of syntactic indirection. Pointers must be dereferenced to get an lvalue to the object they are referencing; reference_wrapper have an implicit conversion operator and can be called as the object they wrap.

     int i; int& ref = std::ref(i); // Okay 
  • reference_wrapper s, unlike pointers, do not have a null state. They must be initialized by reference or another reference_wrapper .

     std::reference_wrapper<int> r; // Invalid 
  • Similarity is the odd semantics of a copy: pointers and reference_wrapper can be reassigned.

+51
Nov 05 '14 at 21:11
source share

There are at least two motivating goals for std::reference_wrapper<T> :

  • It should give referential semantics to the objects passed as a parameter of value into function templates. For example, you may have a large function object that you want to pass to std::for_each() , which takes its parameter of the function object by value. To avoid copying an object, you can use

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

    Passing arguments as std::reference_wrapper<T> to the std::bind() expression is pretty common for binding arguments by reference rather than by value.

  • When using std::reference_wrapper<T> with std::make_tuple() corresponding tuple element becomes T& , not T :

     T object; f(std::make_tuple(1, std::ref(object))); 
+18
Nov 05 '14 at 21:05
source share

You can think of it as a convenient wrapper around links so you can use them in containers.

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

This is basically a CopyAssignable version of T& . Every time you need a link, but it must be assignable, use std::reference_wrapper<T> or its helper function std::ref() . Or use a pointer.

Since it adds an extra layer of indirection, it will be worse (but perhaps carelessly) than T& in cases where they can be used, so you should use it only when you really need to.

Other features:

 sizeof(std::reference_wrapper<T>) == sizeof(T*) // so 8 on a 64-bit box sizeof(T&) == sizeof(T) // so, eg, sizeof(vector<int>&) == 24 
+12
Nov 05
source share

Another difference, in terms of self-documenting code, is that the use of reference_wrapper substantially disavows the ownership of the object. On the contrary, unique_ptr claims ownership, while a bare pointer may or may not be owned (it cannot be recognized without looking at a lot of related code):

 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 
+10
Mar 31 '15 at 20:09
source share



All Articles