C++ Difference between std::ref(T) and T&?

CppNITR picture CppNITR · Oct 20, 2015 · Viewed 31.2k times · Source

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&)?

Answer

Ankit Acharya picture Ankit Acharya · Oct 20, 2015

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.