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
|