107

I have some questions regarding this program:

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
    auto r=ref(x);
    cout<<boolalpha;
    cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
    int x=5;
    foo (x);
    return 0;
}

The output is:

false

I want to know, if std::ref doesn't return the reference of an object, then what does it do? Basically, what is the difference between:

T x;
auto r = ref(x);

and

T x;
T &y = x;

Also, I want to know why does this difference exist? Why do we need std::ref or std::reference_wrapper when we have references (i.e. T&)?

Thierry
  • 1,099
  • 9
  • 19
DevInd
  • 1,645
  • 2
  • 11
  • 17
  • 2
    Possible duplicate of [How is tr1::reference\_wrapper useful?](http://stackoverflow.com/questions/193703/how-is-tr1reference-wrapper-useful) – anderas Oct 20 '15 at 15:47
  • Hint: what happens if you do `x = y;` in both cases? – juanchopanza Oct 20 '15 at 15:47
  • 2
    In addition to my duplicate flag (last comment): See for example http://stackoverflow.com/questions/31270810/difference-between-and-stdreference-wrapper and http://stackoverflow.com/questions/26766939/difference-between-stdreference-wrapper-and-simple-pointer – anderas Oct 20 '15 at 15:48
  • 2
    @anderas it's not about usefulness, it's about the difference mainly – DevInd Oct 20 '15 at 15:48
  • @CppNITR Then see the questions I linked a few seconds before your comment. Especially the second one is useful. – anderas Oct 20 '15 at 15:49
  • @anderas http://stackoverflow.com/questions/31270810/difference-between-and-stdreference-wrapper is quite close but the response and answers are not satisfactory could u explain this question – DevInd Oct 20 '15 at 15:51
  • Whenever I look at one of these things, and think about what its implications might be on the design on complex (especially generic) data structures - I sigh in despair of how richly hard of a language C++ really is. – einpoklum Oct 20 '15 at 19:36

4 Answers4

108

Well ref constructs an object of the appropriate reference_wrapper type to hold a reference to an object. Which means when you apply:

auto r = ref(x);

This returns a reference_wrapper and not a direct reference to x (ie T&). This reference_wrapper (ie r) instead holds T&.

A reference_wrapper is very useful when you want to emulate a reference of an object which can be copied (it is both copy-constructible and copy-assignable).

In C++, once you create a reference (say y) to an object (say x), then y and x share the same base address. Furthermore, y cannot refer to any other object. Also you cannot create an array of references ie code like this will throw an error:

#include <iostream>
using namespace std;

int main()
{
    int x=5, y=7, z=8;
    int& arr[] {x,y,z};    // error: declaration of 'arr' as array of references
    return 0;
}

However this is legal:

#include <iostream>
#include <functional>  // for reference_wrapper
using namespace std;

int main()
{
    int x=5, y=7, z=8;
    reference_wrapper<int> arr[] {x,y,z};
    for (auto a: arr)
        cout << a << " ";
    return 0;
}
/* OUTPUT:
5 7 8
*/

Talking about your problem with cout << is_same<T&,decltype(r)>::value;, the solution is:

cout << is_same<T&,decltype(r.get())>::value;  // will yield true

Let me show you a program:

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;

int main()
{
    cout << boolalpha;
    int x=5, y=7;
    reference_wrapper<int> r=x;   // or auto r = ref(x);
    cout << is_same<int&, decltype(r.get())>::value << "\n";
    cout << (&x==&r.get()) << "\n";
    r=y;
    cout << (&y==&r.get()) << "\n";
    r.get()=70;
    cout << y;
    return 0;
}
/* Ouput:
true
true
true
70
*/

See here we get to know three things:

  1. A reference_wrapper object (here r) can be used to create an array of references which was not possible with T&.

  2. r actually acts like a real reference (see how r.get()=70 changed the value of y).

  3. r is not same as T& but r.get() is. This means that r holds T& ie as its name suggests is a wrapper around a reference T&.

I hope this answer is more than enough to explain your doubts.

Jeinzi
  • 153
  • 3
  • 8
Ankit Acharya
  • 2,833
  • 3
  • 18
  • 29
  • 4
    1: No, a `reference_wrapper` can be _reassigned_, but it cannot "hold reference to more than one objects". 2/3: Fair point about where `.get()` is appropriate - _but_ unsuffixed `r` _can_ be used the same as `T&` in cases where `r`'s conversion `operator` can be invoked unambiguously - so no need to call `.get()` in many cases, including several in your code (which is difficult to read due to lack of spaces). – underscore_d Dec 11 '15 at 17:00
  • @underscore_d `reference_wrapper` can hold an array of references if you are not sure then you can try it yourself. Plus `.get()` is used when you want to change the value of the object the `reference_wrapper` is holding` ie `r=70` is illegal so you have to use `r.get()=70`. Try yourself !!!!!! – Ankit Acharya Dec 12 '15 at 14:03
  • Show me a single `reference_wrapper` holding more than one reference. – underscore_d Dec 12 '15 at 14:55
  • 2
    Not really. First, let's use accurate wording. You are showing a reference_wrapper holding _a reference to an array_ - not a reference_wrapper that itself _contains "reference to more than one objects"_. The wrapper only holds one reference. Secondly, I can get a native reference to an array just fine - are you sure you didn't just forget the (parentheses) around `int a[4]{1, 2, 3, 4}; int (&b)[4] = a;`? reference_wrapper is not special here since native `T&` _does_ work. – underscore_d Dec 12 '15 at 21:03
  • Like an array is used to hold one or more object, a `reference_wrapper` to an array will also hold references to multiple objects. Only because it refers to an array doesn't mean that it doesn't refer to more than one object (in fact it actually does). Of course it's not a special case, rather `array of references` is a special case of `reference_wrapper` (which I have shown in my answer) – Ankit Acharya Dec 13 '15 at 06:26
  • "This reference_wrapper (ie r) instead holds T&" - doesn't it hold `T*` ? – M.M Dec 13 '15 at 08:43
  • 1
    @AnkitAcharya Yes :-) but to be precise, _effective_ result aside, _itself_ only refers to one object. Anyway, you're of course right that unlike a normal ref, the `wrapper` can go in a container. This is handy, but I think people misinterpret this as more advanced than it really is. If I want an array of 'refs', I usually skip the middleman with `vector`, which is what the `wrapper` boils down to... and hope the anti-pointer purists don't find me. The convincing use-cases for it are different and more complex. – underscore_d Dec 13 '15 at 10:45
  • @M.M, yes actually it holds `T*` which is why it is able to refer to more than one object but it's behaviour altogether is more similar to `T&` than `T*` – Ankit Acharya Dec 13 '15 at 15:33
  • 1
    "A reference_wrapper is very useful when you want to emulate a reference of an object which can be copied." But doesn't that defeat the purpose of a reference? You're referring to the actual thing. That's what a reference IS. A reference that can be copied, seems pointless, because if you want to copy it, then you don't want a reference in the first place, you should just copy the original object. It seems like an unnecessary layer of complexity to solve a problem which doesn't need to exist in the first place. – stu Jun 14 '17 at 19:43
  • @stu Coping a reference is useful. This is because you are not copying the value of the reference, but cloning the reference as (yet another) reference to the original object. This is much like coping a pointer. A reason to use `reference_wrapper` over `T *` is to make it crystal clear that `delete` should not be used. – Thomas Eding Mar 19 '19 at 03:47
  • so.... why can't you just make a reference to a reference? int a = 1; int &b = a; int &c = b; – stu Mar 21 '19 at 00:45
60

std::reference_wrapper is recognized by standard facilities to be able to pass objects by reference in pass-by-value contexts.

For example, std::bind can take in the std::ref() to something, transmit it by value, and unpacks it back into a reference later on.

void print(int i) {
    std::cout << i << '\n';
}

int main() {
    int i = 10;

    auto f1 = std::bind(print, i);
    auto f2 = std::bind(print, std::ref(i));

    i = 20;

    f1();
    f2();
}

This snippet outputs :

10
20

The value of i has been stored (taken by value) into f1 at the point it was initialized, but f2 has kept an std::reference_wrapper by value, and thus behaves like it took in an int&.

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 2
    @CppNITR sure ! Give me a moment to assemble a small demo :) – Quentin Oct 20 '15 at 15:51
  • 1
    what about the difference between T& & ref(T) – DevInd Oct 20 '15 at 16:01
  • 3
    @CppNITR `std::ref(T)` returns a `std::reference_wrapper`. It's little more than a wrapped pointer, but is recognize by the library as "hey, I'm supposed to be a reference ! Please turn me back into one once you're done passing me around". – Quentin Oct 20 '15 at 16:32
43

A reference (T& or T&&) is a special element in C++ language. It allows to manipulate an object by reference and has special use cases in the language. For example, you cannot create a standard container to hold references: vector<T&> is ill formed and generates a compilation error.

A std::reference_wrapper on the other hand is a C++ object able to hold a reference. As such, you can use it in standard containers.

std::ref is a standard function that returns a std::reference_wrapper on its argument. In the same idea, std::cref returns std::reference_wrapper to a const reference.

One interesting property of a std::reference_wrapper, is that it has an operator T& () const noexcept;. That means that even if it is a true object, it can be automatically converted to the reference that it is holding. So:

  • as it is a copy assignable object, it can be used in containers or in other cases where references are not allowed
  • thanks to its operator T& () const noexcept;, it can be used anywhere you could use a reference, because it will be automatically converted to it.
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

Added an example to show the difference in value you get when you pass the T& and ref(T) arguments in the bind function.

std::bind copies the argument provided unless it is passed by std::ref()/std::cref().

    void f(int r1, int& r2, int w1, int& w2)
    {
        std::cout << r1 << r2 << w1 << w2; // 5 5 10 10
        r1 = 9, r2 = 9, w1 = 9, w2 = 9;
    }

    int main()
    {
        int w1 = 5, w2 = 5, n1 = 5, n2 = 5;
        int& r1 = n1;
        int& r2 = n2;
        std::function<void()> bound_f = std::bind(f, r1, r2, std::ref(w1), std::ref(w2)); 
        r1 = 10, r2 = 10, w1 = 10, w2 = 10;
        bound_f();                          //  5  5 10 10
        std::cout << r1 << r2 << w1 << w2;  // 10 10 10  9
    }
SridharKritha
  • 8,481
  • 2
  • 52
  • 43