Re: move semantic implementation
On 11 Feb., 21:25, Krzysztof Poc wrote:
[...]
I read that "move" may take both lvalue and rvalue. In the
implementation below I can see that move may take rvalue
only (_Tp&& as an argument). On the other hand when I tested
it I found that below move implementation may take both rvalue
and lvalue (as written in a documentation). So please explain
me what is incorrect in my understanding.
Another thing that I don't understand is the trick with a
remove_reference. I read in a documentation of move that it
returns the rvalue. Keeping in mind that information I assumed
that only the third definition of a remove_reference structure
in a below example is required. Unfortunately it turns out
that all 3 implementations are required for the code to
compile successfully. Could you please explain me why is that.
Both things are closely related, actually.
template<typename _Tp>
inline typename remove_reference<_Tp>::type&&
move(_Tp&& __t)
{
return __t;
}
This is not valid C++11. You probably have an old GCC/libstdc++
version. To make it compile under C++11 you have to replace "return
__t;" with "return static_cast<typename
remove_reference<_Tp>::type&&>(__t);".
As for your questions: There is a thing called reference collapsing:
T T&& T&
------------------
int int&& int&
int&& int&& int&
int& int& int&
^^^^
So, you can't be sure that T&& is actually an rvalue reference type.
It might be an lvalue reference type depending on T. T&& is an lvalue
reference type when T is an lvalue reference type.
Then, there is a special template argument deduction rule. If your
function template takes a parameter of type T&& and T is to be
deduced, T will be deduced to be an lvalue reference type if you pass
an lvalue to the function:
template<class T>
void foo(T&& p);
int main() {
int i=0;
foo(i+0); // --> T=int, T&&=int&&
foo(i); // --> T=int&, T&&=int&
}
So, you see that foo also accepts lvalues. The nice thing about it is
that the "valueness" of the argument is encoded in T so that it can be
restored with something like std::forward<T>(p). That's how perfect
forwarding is implemented.
Back to move:
template<typename _Tp>
inline typename remove_reference<_Tp>::type&&
move(_Tp&& __t)
If you invoke move with an lvalue of type std::string, _Tp will be
deduced to be std::string& which makes decltype(__t) to be
std::string& as well. But std::move is supposed to always return
unnamed rvalue references. That means _Tp&& would be the wrong return
type. We have to remove any "&" from _Tp first. This is done via
remove_reference<_Tp>::type.
Cheers!
SG