move函数的作用就是把传入的参数t的类型转换为右值引用,至于后续的所有权转交问题,是由移动构造函数或移动赋值函数完成的,不要搞混了!
/*源代码*/
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
remove_reference是一个模板,有一个模板参数和一个type的public成员。
看一个例子:
string s1("hi!"),s2;
s2 = std::move(string("bye"));//传入的参数是右值
s2 = std::move(s1);//传入的参数是左值
在第一个赋值中,传递给move的实参是右值,即向一个右值引用传递一个右值
推断出的T为:string
remove_reference<string>
的type成员是string
move的返回类型是string&&
move的函数参数t的类型为string&&
即调用:string&& move(string &&t)
在第二个赋值中,传递给move的实参是左值,
推断出T的类型为string&
remove_reference<string&>
的type成员是string
move的返回类型是string&&
move的函数参数t是string & &&
,会折叠成string&
即调用:string&& move(string &t)
,static_cast<string&&>(t)
,强制将t转换为string&&
当时我有这样的疑问,我传进去的是左值,并不是一个左值引用,为什么编译器会自动把T推导成string&呢。首先模板参数是T&& t
,如果我传进去的t的类型是string,那么T&&就是string&&,这就变成了右值引用,显然t的类型和T&&不对等,这不是我们可以接受的。但是如果我传进去的t的类型是string&,那么T&&就是string&&&,引用折叠后就是string&,这是我们可以接受的。
右值引用:为了支持移动操作,新标准引入了一种新的类型----->右值引用,所谓右值引用就是必须绑定到右值的引用。我们通过&& 而不是&来获得右值引用。右值引用只能绑定到一个将要销毁的对象上面。有着完全相反的绑定特性,我们可以将一个右值引用绑定到这类表达式上,但是不能将一个右值引用直接绑定到左值上。
int i = 42;
int &r1 = i; //正确
int &r2 = r1; //正确
int &&rr1 = i; //错误,不能将一个右值引用绑定到一个左值上
int &r2 = i * 42; //错误,i*42是一个右值,不能将一个左值引用绑定到一个右值上
const int &r3 = i *42; //正确,可以将一个const的左引用绑定到一个右值上
int &&rr2 = i * 42; //正确
将亡值
在C++11中右值又分为纯右值和将亡值。其中纯右值的概念等同于我们在C++98标准中右值的概念;将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),比如返回右值引用T&&的函数返回值、std::move的返回值,或者转换为T&&的类型转换函数的返回值。将亡值可以理解为通过“盗取”其他变量内存空间的方式获取到的值。在确保其他变量不再被使用、或即将被销毁时,通过“盗取”的方式可以避免内存空间的释放和分配,能够延长变量值的生命期。