std::move()如何将值转移到RValues中?
How does std::move() transfer values into RValues?
我只是发现自己没有完全理解std::move()
的逻辑。
起初,我谷歌了一下,但似乎只有关于如何使用std::move()
的文档,而不是它的结构如何工作。
我的意思是,我知道模板成员函数是什么,但是当我在VS2010中查看std::move()
定义时,它仍然令人困惑。
std::move()的定义如下:
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);
}
首先让我感到奇怪的是参数,(_Ty&&_Arg),因为当我像下面这样调用函数时,
// main()
Object obj1;
Object obj2 = std::move(obj1);
基本上等于
// std::move()
_Ty&& _Arg = Obj1;
但是正如你已经知道的,你不能直接链接一个左值到一个右值引用,这让我认为它应该是这样的。
_Ty&& _Arg = (Object&&)obj1;
然而,这是荒谬的,因为std::move()必须对所有的值都有效。
所以我想要完全理解它是如何工作的,我也应该看一下这些结构体。
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;
};
不幸的是,它仍然令人困惑,我不明白。
我知道这都是因为我缺乏基本的c++语法技能。我想彻底了解这些工作是如何进行的,任何我能在网上找到的文件都会非常受欢迎。(如果你能解释一下,那就太棒了)
我们从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
和我们的move
模板实例化如下:
// move with [T = Object]:
remove_reference<Object>::type&& move(Object&& arg)
{
return static_cast<remove_reference<Object>::type&&>(arg);
}
由于remove_reference
将T&
转换为T
或T&&
转换为T
,并且Object
不是引用,因此我们的最终函数是:
Object&& move(Object&& arg)
{
return static_cast<Object&&>(arg);
}
现在,你可能会想:我们还需要演员吗?答案是:是的,我们有。原因很简单;命名的右值引用被视为左值(标准禁止从左值到右值引用的隐式转换)。
下面是用lvalue: 调用
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);
}
同样,remove_reference
将Object&
转换为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);
}
并使用lvalue实例化。
// 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);
}
_Ty是一个模板参数,在这种情况下
Object obj1;
Object obj2 = std::move(obj1);
_Ty is type "Object &"
这就是为什么_Remove_reference是必要的。
应该更像
typedef Object& ObjectRef;
Object obj1;
ObjectRef&& obj1_ref = obj1;
Object&& obj2 = (Object&&)obj1_ref;
如果我们不删除引用,它就像我们在做
Object&& obj2 = (ObjectRef&&)obj1_ref;
但ObjectRef&和简化为Object &,不能绑定到obj2。
减少这种方式的原因是为了支持完美转发。
- 我是否需要在下一次转移时将所有权*转移回转移队列
- 如何将元素从向量转移到新数组?
- 为什么系统函数总是在C++中返回已转移的退出状态?
- C++:将 UDP 袜子转移到新位置
- 为什么最后一个推回向量的对象的字段会转移到向量的其他对象?
- 如何在MPI中将矩阵从一个进程转移到另一个进程
- Qt将鼠标移动事件转移到新窗口
- 带有 std::minmax 和 rvalues 的结构化绑定
- 将进程的执行从线程1转移到线程2
- 如何在SOCI中注册数据库故障转移回调?
- 这个C++结构如何从函数转移到"你的"成员
- 如果A("whole")有一组B("parts"),如何同步"part" B从A "whole"到另一个A的转移?
- 将 ulong 从 c++ 转移到 c#
- 错误:静态断言失败:std ::线程参数必须在转换为rvalues后不可行
- C++将SDL_renderer转移到班级
- 将矩阵转移到阵列中
- 从Turbo转移到Code :: Blocks
- 如何将枯竭的健康转移到下一个循环中
- OPENCL-如何使辅助功能返回阵列并将内存的部分从恒定内存空间转移到私有
- std::move()如何将值转移到RValues中?