C++ Difference Between Std::ref(T) And T&?
Answer :
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_wrapperobject (herer) can be used to create an array of references which was not possible withT&.ractually acts like a real reference (see howr.get()=70changed the value ofy).ris not same asT&butr.get()is. This means thatrholdsT&ie as its name suggests is a wrapper around a referenceT&.
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&.
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.
Comments
Post a Comment