我们从 move 函数开始(我稍微清理了一下):
template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
return static_cast<typename remove_reference<T>::type&&>(arg);
}
让我们从更简单的部分开始 - 也就是说,当使用右值调用函数时:
Object a = std::move(Object());
// Object() is temporary, which is prvalue
and our move
模板实例化如下:
// move with [T = Object]:
remove_reference<Object>::type&& move(Object&& arg)
{
return static_cast<remove_reference<Object>::type&&>(arg);
}
Since remove_reference
皈依者T&
to T
or T&&
to T
, and Object
不是参考,我们最终的函数是:
Object&& move(Object&& arg)
{
return static_cast<Object&&>(arg);
}
现在,您可能想知道:我们还需要演员阵容吗?答案是:是的,我们愿意。原因很简单;命名右值引用is被视为左值(标准禁止从左值到右值引用的隐式转换)。
这是当我们调用时会发生的情况move
与左值:
Object a; // a is lvalue
Object b = std::move(a);
以及相应的move
实例化:
// move with [T = Object&]
remove_reference<Object&>::type&& move(Object& && arg)
{
return static_cast<remove_reference<Object&>::type&&>(arg);
}
Again, remove_reference
皈依者Object&
to Object
我们得到:
Object&& move(Object& && arg)
{
return static_cast<Object&&>(arg);
}
现在我们进入棘手的部分:什么是Object& &&
甚至意味着它如何绑定到左值?
为了允许完美转发,C++11标准提供了引用折叠的特殊规则,如下:
Object & & = Object &
Object & && = Object &
Object && & = Object &
Object && && = Object &&
正如你所看到的,根据这些规则Object& &&
实际上意味着Object&
,这是允许绑定左值的普通左值引用。
最终函数是:
Object&& move(Object& arg)
{
return static_cast<Object&&>(arg);
}
这与之前的右值实例化没有什么不同——它们都将其参数转换为右值引用,然后返回它。不同之处在于,第一个实例化只能与右值一起使用,而第二个实例化则与左值一起使用。
解释为什么我们需要remove_reference
再多一点,让我们尝试一下这个功能
template <typename T>
T&& wanna_be_move(T&& arg)
{
return static_cast<T&&>(arg);
}
并用左值实例化它。
// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
return static_cast<Object& &&>(arg);
}
应用上面提到的引用折叠规则,您可以看到我们得到了无法使用的函数,如下所示move
(简单来说,你用左值调用它,你就得到左值)。如果有的话,这个函数就是恒等函数。
Object& wanna_be_move(Object& arg)
{
return static_cast<Object&>(arg);
}