145

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

nbro
  • 15,395
  • 32
  • 113
  • 196
Laurynas Lazauskas
  • 2,855
  • 2
  • 21
  • 26
  • 4
    It's basically a pointer that you use `.` with instead of `->` – M.M Nov 05 '14 at 21:07
  • 9
    @M.M No, using `.` doesn't work the way you suggest it does (unless at some point the operator dot proposal is adopted and integrated :) ) – Columbo Apr 13 '16 at 16:45
  • 4
    It is questions like this that make me unhappy when I have to work with the new C++. – Nils Jul 25 '19 at 10:28
  • 1
    To follow up Columbo, std::reference_wrapper is used with its `get()` member-function or with its implicit conversion back to the underlying type. – Max Barraclough May 30 '20 at 13:09

4 Answers4

123

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 STL 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…

  • reference_wrappers overload operator() so they can be called just like the function objects they refer to:

     std::ref(myEngine)() // Valid expression, modifies myEngines state
    
  • …(un)like ordinary references, copying (and assigning) reference_wrappers just assigns the 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 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

  • There is no additional level of syntactical indirection. Pointers have to be dereferenced to obtain an lvalue to the object they refer to; reference_wrappers have an implicit conversion operator and can be called like the object they wrap.

     int i;
     int& ref = std::ref(i); // Okay
    
  • reference_wrappers, unlike pointers, don't have a null state. They have to be initialized with either a reference or another reference_wrapper.

     std::reference_wrapper<int> r; // Invalid
    
  • A similarity are the shallow copy semantics: Pointers and reference_wrappers can be reassigned.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Is `std::make_tuple(std::ref(i));` superior to `std::make_tuple(&i);` in some way? – Laurynas Lazauskas Nov 05 '14 at 21:36
  • 7
    @LaurynasLazauskas It's different. The latter you showed saves a pointer to `i`, not a reference to it. – Columbo Nov 05 '14 at 21:38
  • Hm... I guess I still can't distinct these two as well as I would like... Well, thank you. – Laurynas Lazauskas Nov 05 '14 at 21:41
  • @Columbo How is an array of reference wrappers possible if they don't have null state? Don't arrays usually start with all elements set to null state? – anatolyg Nov 06 '14 at 14:14
  • 2
    @anatolyg What hinders you from initializing that array? – Columbo Nov 06 '14 at 16:12
  • 1
    Pointers can also be declared such to not have a null state with [not_null](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i12-declare-a-pointer-that-must-not-be-null-as-not_null) – Rufus Feb 16 '17 at 04:17
  • @Woofas That makes no difference. If I take a parameter that is a pointer, I don't know whether it was initialized using `not_null`. – Columbo Feb 16 '17 at 12:24
  • @Columbo It's possible to specify a parameter as `not_null` such that passing a null pointer to it becomes invalid – Rufus Feb 17 '17 at 03:33
  • @Woofas Oh, sorry, I thought that was a function (I'm used to UpperCamelCase for class names). Sure, you could. – Columbo Feb 17 '17 at 09:42
  • @Woofas Interesting, but I don't know why we would use a pointer in that case, and not just a reference. Did they ever explain why `not_null` is preferable? I don't see it, but I'm not known for being very imaginative... – underscore_d May 20 '17 at 19:13
  • 2
    `reference_wrappers, unlike pointers, don't have a null state. They have to be initialized with either a reference or another reference_wrapper. ` Since C++17 you can use a `std::optional` to have a reference without initialization: `std::optional> x; auto y = 4; x = y;` Accessing to it is a bit verbose though: `std::cout << x.value().get();` – dteod Aug 31 '19 at 23:52
  • I think `optional` is better? – Columbo Sep 01 '19 at 14:04
  • 1
    @Columbo: "[...] a program is ill-formed if it instantiates an optional with a reference type" https://en.cppreference.com/w/cpp/utility/optional – Johann Gerell Aug 03 '21 at 08:30
36

Another difference, in terms of self-documenting code, is that using a reference_wrapper essentially disavows ownership of the object. In contrast, a unique_ptr asserts ownership, while a bare pointer might or might not be owned (it's not possible to know without looking at lots 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
Edward Loper
  • 15,374
  • 7
  • 43
  • 52
  • 3
    unless it's pre-c++11 code, the first example should imply optional, unowned values, for example for a cache lookup based on index. It would be nice if std provided us with something standard to represent a non-null, owned value (unique & shared variants) – Bwmat Dec 24 '16 at 07:19
  • It's maybe not as important in C++11, where bare pointers will nearly always be borrowed values anyway. – bjaastad_e Apr 23 '20 at 15:44
  • `reference_wrapper` is superior to raw pointers not only because it is clear that it is non-owning, but also because it cannot be `nullptr` (without shenanigans) and thus users know they can't pass `nullptr` (without shenanigans) and you know you don't have to check for it. – underscore_d Jul 23 '20 at 14:37
  • @underscore_d What do you mean "without shenanigans"? Is it possible **with** shenanigans? I am very curious – Spyros Mourelatos Jul 27 '21 at 10:20
  • 1
    @SpyrosMourelatos It is possible, albeit with undefined behaviour and hence totally inadvisable... to form a 'null reference'. One can then shove the resulting invalid reference into a `reference_wrapper`. But creating/using such references have undefined behaviour, hence my - overly generous! - labelling of it as "shenanigans". See [Is null reference possible?](https://stackoverflow.com/questions/4364536/is-null-reference-possible) – underscore_d Jul 28 '21 at 11:08
  • @underscore_d Thank you very much , with your help I can finally force my colleagues at work to commit suicide. :P – Spyros Mourelatos Jul 29 '21 at 08:06
  • @Bwmat non-null unique ownership is currently not possible in the language in a reasonable way. – Jeff Garrett Jan 22 '22 at 15:19
35

There are, at least, two motivating purposes of std::reference_wrapper<T>:

  1. It is to give reference semantics to objects passed as value parameter to function templates. For example, you may have a large function object you want to pass to std::for_each() which takes its function object parameter by value. To avoid copying the object, you can use

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

    Passing arguments as std::reference_wrapper<T> to an std::bind() expression is quite common to bind arguments by reference rather than by value.

  2. When using an std::reference_wrapper<T> with std::make_tuple() the corresponding tuple element becomes a T& rather than a T:

    T object;
    f(std::make_tuple(1, std::ref(object)));
    
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Can you please give a code example for the first case? – user1708860 Nov 05 '14 at 21:09
  • 1
    @user1708860: you mean other than the one given...? – Dietmar Kühl Nov 05 '14 at 21:11
  • I mean actual code that goes with the std::ref(fun) because i don't understand how is used (unless fun is an object and not a function...) – user1708860 Nov 05 '14 at 21:12
  • 3
    @user1708860: yes, most likely `fun` is a function object (i.e. an object of a class with a function call operator) and not a function: if `fun` happens to be an actual function, `std::ref(fun)` have no purpose and make the code potentially slower. – Dietmar Kühl Nov 05 '14 at 21:14
32

You can think of it as a convenience wrapper around references so that 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

It's basically a CopyAssignable version of T&. Any time you want a reference, but it has to be assignable, use std::reference_wrapper<T> or its helper function std::ref(). Or use a pointer.


Other quirks: 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

And comparison:

int i = 42;
assert(std::ref(i) == std::ref(i)); // ok

std::string s = "hello";
assert(std::ref(s) == std::ref(s)); // compile error
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Why would one use `std::vector> vec;` instead of `std::vector vec;`? – Laurynas Lazauskas Nov 05 '14 at 21:23
  • 1
    @LaurynasLazauskas One could call function objects contained in the wrapper directly. That is also explained in my answer. – Columbo Nov 05 '14 at 21:40
  • 2
    Since reference implementation is just a pointer inside I can not understand why wrappers add any indirection or performance penalty – Riga Nov 05 '14 at 22:20
  • 4
    It should be no more indirection than a simple reference when it comes to a release code – Riga Nov 06 '14 at 05:07
  • 3
    I would expect the compiler to inline the trivial `reference_wrapper` code, making it identical to code that uses a pointer or reference. – David Stone Nov 25 '14 at 14:44
  • 6
    @LaurynasLazauskas: `std::reference_wrapper` has the guarantee that the object is never null. Consider a class member `std::vector`. You have to examine all of the class code to see if this object can ever store a `nullptr` in the vector, whereas with `std::reference_wrapper`, you are guaranteed to have valid objects. – David Stone Nov 25 '14 at 14:46
  • I fail to see how your "quirks" are quirks. Both are by definition: (1) A `reference_wrapper` must refer to its object via a pointer (because it must be assignable, otherwise we wouldn't need this class, etc.), and that's all it needs. (2) A real reference has no `sizeof` according to the language, so the operator always returns the `sizeof` the referred type (the fact that a reference within an object is implemented as a pointer and evident in the `sizeof` said object is an implementation detail). – underscore_d Sep 24 '18 at 18:35
  • 2
    @underscore_d I fail to see how something being true by definition somehow prevents it from being a quirk. Given a `vector v;`, `std::vector x{v, v}` and `std::vector y{v}` have different types. That's true by definition. But also a quirk of CTAD rules. – Barry Sep 24 '18 at 18:39