I don't understand very well the std::move function
template <class T>
typename remove_reference<T>::type&&
move(T&& a)
{
return a;
}
why remove_reference ?
could someone give me a simple explanation ?
Think about what happens if T is an lvalue reference, for example MyClass &. In that case, T && would become MyClass & &&, and due to reference collapsing rules, this would be transformed into MyClass & again. To achieve the right result, typename remove_reference<MyClass&>::type&& first removes any reference decorations from the type, so MyClass & is mapped to MyClass, and then the rvalue reference is applied to it, yielding MyClass &&.
Because rvalue reference to lvalue reference would decay to lvalue reference, and returing lvalue reference would have different semantics from those you would expect from move.
Edit:
Huh, why the downvote? Check out this code:
template < typename T > T&& func(T&& x) { return x; }
int main()
{
int x;
int &y = func(x);
}
Further reading: http://www.justsoftwaresolutions.co.uk/cplusplus/rvalue_references_and_perfect_forwarding.html
Related
I know that the second overload of std::forward:
template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;
is used for rvalues (as stated by Howard Hinnant in his answer: How does std::forward receive the correct argument?)
There is an example of when this overload is used at cppreference.com (that is also mentioned in How does std::forward receive the correct argument? by Praetorian):
Forwards rvalues as rvalues and prohibits forwarding of rvalues as lvalues This overload makes it possible to forward a result of an expression (such as function call), which may be rvalue or lvalue, as the original value category of a forwarding reference argument.
For example, if a wrapper does not just forward its argument, but calls a member function on the argument, and forwards its result:
// transforming wrapper
template<class T>
void wrapper(T&& arg)
{
foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get()));
}
where the type of arg may be
struct Arg
{
int i = 1;
int get() && { return i; } // call to this overload is rvalue
int& get() & { return i; } // call to this overload is lvalue
};
I really don't get this example. Why is the outer forward forward<decltype(forward<T>(arg).get())> even needed?
Cppreference states:
This overload makes it possible to forward a result of an expression (such as function call), which may be rvalue or lvalue, as the original value category of a forwarding reference argument.
As an example:
void func(int& lvalue)
{
std::cout << "I got an lvalue!" << std::endl;
}
void func(int&& rvalue)
{
std::cout << "I got an rvalue!" << std::endl;
}
template <typename T>
T&& myForward(typename std::remove_reference_t<T>& t)
{
return static_cast<T&&>(t);
}
struct foo
{
int i = 42;
int& get()& { return i; }
int get()&& { return i; }
};
template <typename T>
void wrapper(T&& t)
{
func(myForward<T>(t).get());
}
int main()
{
foo f;
wrapper(f);
wrapper(foo());
return 0;
}
This prints:
I got an lvalue!
I got an rvalue!
just fine, without the outer forward, while it also forwards the "result of an expression [...] as the original value category of a forwarding reference argument." It does not even need the second overload of std::forward. This overload is only necessary when calling func() like this:
func(myForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get()));
Still, I can't wrap my head around why anyone would need to add the outer forward.
Edit: Edit moved to follow-up question: RValue-reference overload of std::forward potentially causing dangling reference?
Why is the outer forward forward<decltype(forward<T>(arg).get())> even needed?
It's not. The expression already is of its own correct value category. In C++17 (when returning by value bigger types) it's even a pessimization. All it does is turn a potential prvalue into an xvalue, and inhibiting copy elision. I'm tempted to say it's cargo cult programming.
I am refreshing my memory on how perfect forwarding works in C++. I realize that a call to std::forward is forced to provide an explicit template parameter for a reason (i. e. when dealing with rvalue references that are actually lvalues), however when doing a sanity check on actual code, I was surprised by this (somewhat related) scenario:
#include <iostream>
#include <utility>
#include <type_traits>
template<class T>
T&& fwd(T& t) {
return static_cast<T&&>(t);
}
template<class T>
T&& fwd(T&& t) {
return static_cast<T&&>(t);
}
int main()
{
int lnum = 3;
if (std::is_rvalue_reference<decltype(fwd(lnum))>::value)
std::cout << "It's rref." << std::endl; // this get's printed on screen
else
std::cout << "It's lref." << std::endl;
return 0;
}
If I understand reference collapsing correctly (and I believe I do), type deduction should go like this:
int& && fwd(int& & t) {
return static_cast<int& &&>(t);
}
leading to
int& fwd(int& t) {
return static_cast<int&>(t);
}
Clearly that's not the case. What am I missing here?
Actually, no referencing collapsing occurs. The relevant function template to pay attention to, i.e., the one selected, is:
template<class T>
T&& fwd(T& t) { // <-- not a forwarding reference
return static_cast<T&&>(t);
}
Note that this function template has no forwarding references – the function parameter, t, is just an lvalue reference (T& t).
The T template parameter is deduced to int – not int& – because t is not a forwarding reference but just an lvalue reference. If you simply replace T by int in the function template above, then you will obtain:
template<class T>
int&& fwd(int& t) {
return static_cast<int&&>(t);
}
No reference collapsing is applied as there is no such a thing here that would otherwise end up becoming a reference to a reference (e.g., int& && or int&& &&).
Firstly, the function that is called is T&& fwd(T& t). As such, there is no forwarding reference parameter. The parameter is an lvalue reference and the deduced T is int. As such, there are no references to collapse and the static cast produces int&&.
If the called function had been T&& fwd(T&& t) (i.e. if the better matching overload didn't exist), then your explanation of reference collapsing would be correct (except for the parameter which would be int& && which also collapses int&) and the return type would indeed be lvalue reference.
I know that the second overload of std::forward:
template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;
is used for rvalues (as stated by Howard Hinnant in his answer: How does std::forward receive the correct argument?)
There is an example of when this overload is used at cppreference.com (that is also mentioned in How does std::forward receive the correct argument? by Praetorian):
Forwards rvalues as rvalues and prohibits forwarding of rvalues as lvalues This overload makes it possible to forward a result of an expression (such as function call), which may be rvalue or lvalue, as the original value category of a forwarding reference argument.
For example, if a wrapper does not just forward its argument, but calls a member function on the argument, and forwards its result:
// transforming wrapper
template<class T>
void wrapper(T&& arg)
{
foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get()));
}
where the type of arg may be
struct Arg
{
int i = 1;
int get() && { return i; } // call to this overload is rvalue
int& get() & { return i; } // call to this overload is lvalue
};
I really don't get this example. Why is the outer forward forward<decltype(forward<T>(arg).get())> even needed?
Cppreference states:
This overload makes it possible to forward a result of an expression (such as function call), which may be rvalue or lvalue, as the original value category of a forwarding reference argument.
As an example:
void func(int& lvalue)
{
std::cout << "I got an lvalue!" << std::endl;
}
void func(int&& rvalue)
{
std::cout << "I got an rvalue!" << std::endl;
}
template <typename T>
T&& myForward(typename std::remove_reference_t<T>& t)
{
return static_cast<T&&>(t);
}
struct foo
{
int i = 42;
int& get()& { return i; }
int get()&& { return i; }
};
template <typename T>
void wrapper(T&& t)
{
func(myForward<T>(t).get());
}
int main()
{
foo f;
wrapper(f);
wrapper(foo());
return 0;
}
This prints:
I got an lvalue!
I got an rvalue!
just fine, without the outer forward, while it also forwards the "result of an expression [...] as the original value category of a forwarding reference argument." It does not even need the second overload of std::forward. This overload is only necessary when calling func() like this:
func(myForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get()));
Still, I can't wrap my head around why anyone would need to add the outer forward.
Edit: Edit moved to follow-up question: RValue-reference overload of std::forward potentially causing dangling reference?
Why is the outer forward forward<decltype(forward<T>(arg).get())> even needed?
It's not. The expression already is of its own correct value category. In C++17 (when returning by value bigger types) it's even a pessimization. All it does is turn a potential prvalue into an xvalue, and inhibiting copy elision. I'm tempted to say it's cargo cult programming.
Let's have a function called Y that overloads:
void Y(int& lvalue)
{ cout << "lvalue!" << endl; }
void Y(int&& rvalue)
{ cout << "rvalue!" << endl; }
Now, let's define a template function that acts like std::forward
template<class T>
void f(T&& x)
{
Y( static_cast<T&&>(x) ); // Using static_cast<T&&>(x) like in std::forward
}
Now look at the main()
int main()
{
int i = 10;
f(i); // lvalue >> T = int&
f(10); // rvalue >> T = int&&
}
As expected, the output is
lvalue!
rvalue!
Now come back to the template function f() and replace static_cast<T&&>(x) with static_cast<T>(x). Let's see the output:
lvalue!
rvalue!
It's the same! Why? If they are the same, then why std::forward<> returns a cast from x to T&&?
The lvalue vs rvalue classification remains the same, but the effect is quite different (and the value category does change - although not in an observable way in your example). Let's go over the four cases:
template<class T>
void f(T&& x)
{
Y(static_cast<T&&>(x));
}
template<class T>
void g(T&& x)
{
Y(static_cast<T>(x));
}
If we call f with an lvalue, T will deduce as some X&, so the cast reference collapses X& && ==> X&, so we end up with the same lvalue and nothing changes.
If we call f with an rvalue, T will deduce as some X so the cast just converts x to an rvalue reference to x, so it becomes an rvalue (specifically, an xvalue).
If we call g with an lvalue, all the same things happen. There's no reference collapsing necessary, since we're just using T == X&, but the cast is still a no-op and we still end up with the same lvalue.
But if we call g with an rvalue, we have static_cast<T>(x) which will copy x. That copy is an rvalue (as your test verifies - except now it's a prvalue instead of an xvalue), but it's an extra, unnecessary copy at best and would be a compilation failure (if T is movable but noncopyable) at worst. With static_cast<T&&>(x), we were casting to a reference, which doesn't invoke a copy.
So that's why we do T&&.
I have the following piece of code, as an example dec_proxy attempts to reverse the effects of the increment operator upon the type that is executed in a complex function call foo - which btw I cannot change the interface of.
#include <iostream>
template<typename T>
class dec_proxy
{
public:
dec_proxy(T& t)
:t_(t)
{}
dec_proxy<T>& operator++()
{
--t_;
return *this;
}
private:
T& t_;
};
template<typename T, typename S, typename R>
void foo(T& t, S& s, R& r)
{
++t;
++s;
++r;
}
int main()
{
int i = 0;
double j = 0;
short k = 0;
dec_proxy<int> dp1(i);
dec_proxy<double> dp2(j);
dec_proxy<short> dp3(k);
foo(dp1,dp2,dp3);
//foo(dec_proxy<int>(i), <---- Gives an error
// dec_proxy<double>(j), <---- Gives an error
// dec_proxy<short>(k)); <---- Gives an error
std::cout << "i=" << i << std::endl;
return 0;
}
The problem is that for the various types I'd like to use dec_proxy I currently require creating a specialized instance of dec_proxy - it seems like a very messy and limited approach.
My question is: What is the correct way to pass such short-lived temporaries as non-const reference parameters?
Taking Stephen's advice, you should look at the answer to How come a non-const reference cannot bind to a temporary object? and simply add a member function that returns a reference dec_proxy, e.g.:
dec_proxy &ref() { return *this; }
and call foo:
foo(
dec_proxy<int>(i).ref(),
dec_proxy<double>(j).ref(),
dec_proxy<short>(k).ref());
I'm pretty sure that compiles.
Thanks to MSN, the solution:
I don't think it is correct by adding the function template template<typename T> dec_proxy_impl<T>& dec_proxy(T&t).
What it did is just cheating compiler. It will result in runtime error. The function foo requires the lvaue or lvalue reference. But template<typename T> dec_proxy_impl<T>& dec_proxy(T&t) doesn't return a valid lvalue reference. In the implementation, it creates a temporary object, and returns it. After the function call finishes, the temporary object will be destroyed. So the value reference passed into the function foo is wrong. Actually the referenced object has already been destroyed. The ++t;++s;++r are trying to access the invalid objects. The behavior is undefined.
The solution from MSN is correct. The life time of the object dec_proxy<int>(i) is from its declaration to the end of the function call. It makes sure the parameter in the function foo is valid.
What you try to do is to pass a rvalue (your new dec_facade<int>(i)) as an lvalue reference, which explains why it doesn't work.
If you compiler support it, you can use rvalue references, using && type modifier :
(Support for rvalue reference could be enabled by switching on C++0x or C++11 [partial] support)
template<typename T>
void foo(T& t)
{
++t;
}
template<typename T>
void foo(T&& t)
{
++t;
}
But that only one part of the problem. What you try to do, is to pre-increment a temporary value! That's non-sense, as it won't live after that call. Your object will be increment, then destroyed.
One other solution would be to remove the & from your function definition, which would permit it to accept any parameter. But that's perhaps not what you want.