完美转发"/>
万能引用与完美转发
1.1、左/右值引用变量的值类型都是左值:
引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值;引用的值类型和变量类型不一样, 左/右值引用变量的值类型都是左值, 而不是左值引用或者右值引用。
void func2(int&& val) { cout << "右值" << endl; } // 右值引用函数
void func2(int& val) { cout << "左值" << endl; } // 左值引用函数
void func1(int&& val)
{ func2(val); // 此时val是左值,调用第二行的函数,输出“左值”
}
1.2、万能引用(Universal Reference):
使用 T&&
类型的形参既能绑定右值,又能绑定左值,只有发生类型推导的时候,T&&
才表示万能引用 ;否则,表示右值引用。
即万能引用只存在模板中,模板中的&&
不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
1.3、完美转发(perfect forwarding):
是指在函数模板中,完全依照模板参数的类型,将参数传递给函数模板中调用的另一个函数。
模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,我们希望能够在传递(转发)过程中保持它的左值或者右值的属性, 即执行例二后输出右值。
目的: 数据是左值就转发成左值,右值就转发成右值。
// 完美转发原型:
T&& forward(T&& t) { return static_cast<T&&>(t); } // 用法: template<typename T>
void func1(T && val) { func2(std::forward<T>(val)); } // 当传入左值引用
void func1(T& && val) { func2(static_cast<T& &&>(val)); }
// 引用折叠后:
void func1(T& val) { func2(static_cast<T&>(val)); } // 当传入右值引用
void func1(T&& && val) { func2(static_cast<T&& &&>(val)); }
// 引用折叠后:
void func1(T&& val) { func2(static_cast<T&&>(val)); }
即:传入的左值,传递给调用函数仍然为左值,传入的右值,传递给调用函数的仍为右值,不会退化为左值。
总结:
1、完美转发实现了参数在函数调用传递(转发)过程中保持它的左值或者右值的属性不变。
------------------------------------------------------------------------------------------------------------------
1.4、move原理
std::move除了能实现右值引用,同时也能实现对左值的引用。在左值上使用移动语义。
std::move的实现主要依赖于static<T&& >,但同时也会做一些参数推导的工作。其实现如下:
template<typename T>
typename remove_reference<T>::type&& move(T&& t)
{return static_cast<typename remove_reference<T>::type &&>(t);
}
对于右值
有如下代码:std::move(string("dengwen"));
首先模板类型推导确定T的类型为string,得remove_reference::type为string,故返回值和static的模板参数类型都为string &&;而move的参数就是string &&,于是不需要进行类型转换直接返回。
对于左值
引入一条规则:当将一个左值传递给一个参数是右值引用的函数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板参数类型为实参的左值引用。
有如下代码:string str("dengwen"); std::move(str);
此时明显str是一个左值,首先模板类型推导确定T的类型为string &,得remove_reference::type为string。故返回值和static的模板参数类型都为string &&;而move的参数类型为string& &&,折叠后为sting &。
所以结果就为将string &通过static_cast转为string &&。返回string &&。
------------------------------------------------------------------------------------------------
1.5、
默认构造函数
拷贝构造函数
重载赋值函数(不是构造函数)
移动拷贝构造函数
移动重载赋值函数(不是构造函数)
如果用户没有定义任何构造函数,那么编译器为我们生成默认构造函数,如果自行定义了随便什么样的构造函数(包括拷贝构造函数/移动拷贝构造函数,不包含重载赋值函数和移动重载赋值函数;因为他们不是构造函数),那么编译器就不会为我们生成默认构造函数。假如我们也没有自行定义默认构造函数,那么所有使用此类默认构造函数的地方都将编译不通过。
拷贝构造,重载赋值,移动拷贝构造,重载移动赋值,这四个函数编译器会为我们自动生成,除非发生如下情况:
一旦自行定义了拷贝构造,那么移动构造也需要显示定义,同理如果定义了移动构造,那么拷贝构造也要显示定义,因为编译器会将另一个置为 =delete;
编译器创建默认的 “移动拷贝构造” 和 “重载移动赋值” 要满足如下条件:所有非 static 都是可移动的;
拷贝行为 和 移动行为
如果一个类没有定义移动行为(移动拷贝构造和移动赋值构造),但是定义了拷贝行为(拷贝构造和赋值构造),那么试图通过move调用移动行为时不会成功,取而代之,会只用拷贝行为作为替代。
A a1,a2;
a1 = move(a2); //如果没有移动赋值,那么这句话会被编译器翻译为 a1 = a2;
A a3(move(a1)); //如果没有移动拷贝,那么这句话会被编译器翻译为 a3(a1);
更多推荐
万能引用与完美转发
发布评论