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&
)?
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)
std::reference_wrapper can be used as an argument to a template function (or constructor) to avoid specifying the template parameter types explicitly.
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:
A reference_wrapper
object (here r
) can be used to create an array of references which was not possible with T&
.
r
actually acts like a real reference (see how r.get()=70
changed the value of y
).
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.
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&
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With