Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

reference

ref

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

like image 478
DevInd Avatar asked Oct 20 '15 15:10

DevInd


People also ask

What is std:: ref in c++?

Function templates ref and cref are helper functions that generate an object of type std::reference_wrapper, using template argument deduction to determine the template argument of the result. T may be an incomplete type. (since C++20)

What std:: ref used for?

std::reference_wrapper can be used as an argument to a template function (or constructor) to avoid specifying the template parameter types explicitly.


2 Answers

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.

like image 181
Ankit Acharya Avatar answered Oct 14 '22 19:10

Ankit Acharya


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&.

like image 33
Quentin Avatar answered Oct 14 '22 19:10

Quentin