我们从move函数开始(我稍微清理了一下):
模板<类型名T>typename remove_reference<T>::类型&&move(T&&arg){return static_cast<typename remove_reference<T>::type&&>(arg);}
让我们从更简单的部分开始,即当函数使用rvalue调用时:
对象a=std::move(Object());//Object()是临时的,它是prvalue
和我们的移动
模板实例化如下:
//用[T=对象]移动:remove_reference<对象>::type&&move(对象&&arg){return static_cast<remove_reference<Object>:类型&>(arg);}
自删除引用(_R)
转换T型&
到T型
或T型&&
到T型
、和对象
不是参考,我们的最终功能是:
对象和移动(对象和参数){return static_cast<对象(arg);}
现在,你可能会想:我们甚至需要演员吗?答案是:是的,我们有。原因很简单;命名的rvalue引用是被视为左值(标准禁止从左值到右值引用的隐式转换)。
下面是我们打电话时发生的情况移动
使用左值:
对象a;//a是左值对象b=std::移动(a);
和相应的移动
实例化:
//用[T=对象&]移动remove_reference<对象>::type&&move(对象&&&arg){return static_cast<remove_reference<Object>::type>(arg);}
再一次,删除引用
转换对象&
到对象
我们得到:
对象和移动(对象和参数){return static_cast<对象(arg);}
现在我们进入棘手的部分:什么是对象(&O)&&
即使是卑鄙,它又如何与左值绑定?
为了实现完美转发,C++11标准为引用折叠提供了特殊规则,如下所示:
对象&&=对象&Object&&&=对象&Object&&&=对象&Object&&&&=对象&&
正如你所见,根据这些规则对象(&O)&&
实际上是指对象&
,它是允许绑定左值的纯左值引用。
因此,最终功能是:
对象和移动(对象和参数){return static_cast<对象(arg);}
这与之前使用rvalue的实例化没有什么不同,它们都将其参数转换为rvalue引用,然后返回它。不同的是,第一个实例化只能用于rvalue,而第二个实例化则用于lvalue。
解释为什么我们需要删除引用
再多一点,让我们试试这个函数
模板<类型名T>T&&wanna_be_move(T&&arg){return static_cast<T&&>(arg);}
并用左值实例化它。
//wanna_be_move[with T=对象&]对象&&&wanna_be_move(对象&&&参数){return static_cast<对象&&>(arg);}
应用上面提到的引用折叠规则,您可以看到我们得到了一个无法使用的函数移动
(简单地说,你用lvalue调用它,就会得到lvalue)。如果有的话,这个函数就是标识函数。
对象&wanna_be_move(对象&arg){return static_cast<对象->(arg);}