我不完全理解std::move()
的实现。也就是说,我对MSVC标准库中的这种实现感到困惑:
template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{ // forward _Arg as movable
return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
}
当我像这样调用std::move
时:
Object obj1;
Object obj2 = std::move(obj1); // _Ty&& _Arg binds to obj1
. _Arg
引用参数绑定到左值obj1
。你不能直接将一个右值引用绑定到一个左值,这让我认为像(Object&&)
这样的右值引用的强制转换是必需的。然而,这是荒谬的,因为std::move()
必须对所有值都有效。
为了充分理解这是如何工作的,我也看了std::remove_reference
的实现:
template<class _Ty>
struct _Remove_reference
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&>
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&&>
{ // remove rvalue reference
typedef _Ty _Type;
};
不幸的是,它仍然是令人困惑的,我不明白。请帮助我理解std::move
的实现。
2条答案
按热度按时间cgh8pdjw1#
我们从move函数开始(我稍微清理了一下):
让我们从更简单的部分开始-也就是说,当函数被右值调用时:
我们的
move
模板被示例化如下:由于
remove_reference
将T&
转换为T
或将T&&
转换为T
,而Object
不是引用,因此我们的最终函数是:现在,你可能会想:我们还需要演员表吗答案是:有的.原因很简单;命名的右值引用 * 被 * 视为左值(并且标准禁止从左值到右值引用的隐式转换)。
下面是当我们用左值调用
move
时发生的情况:以及相应的
move
示例化:同样,
remove_reference
将Object&
转换为Object
,我们得到:现在我们进入棘手的部分:
Object& &&
是什么意思,它如何绑定到左值?为了允许完美的转发,C++11标准提供了引用折叠的特殊规则,如下所示:
正如你所看到的,在这些规则下,
Object& &&
实际上意味着Object&
,这是一个允许绑定左值的普通左值引用。最终函数是:
这与前面使用右值的示例化没有什么不同-它们都将其参数转换为右值引用,然后返回它。区别在于第一个示例化只能用于右值,而第二个示例化可以用于左值。
为了解释为什么我们需要
remove_reference
多一点,让我们试试这个函数并使用左值示例化它。
应用上面提到的引用折叠规则,你可以看到我们得到了一个不能作为
move
使用的函数(简单地说,你用左值调用它,你会得到左值)。如果有的话,这个函数就是恒等函数。7nbnzgx92#
_Ty是模板参数,在这种情况下,
_Ty是“Object &”类型
这就是为什么_Remove_reference是必要的。
它将很像
如果我们不删除引用,
但是ObjectRef&&简化为Object &,我们无法将其绑定到obj2。
它以这种方式减少的原因是支持完美转发。看这张纸。