右值
左值 lvalue:可被寻址(有名)的值
右值 rvalue:不可被寻址(无名)的值(如将亡值,字面常量)
将亡值:函数以值返回(区别于地址返回)时,所构建的不具名实体即为将亡值。将亡值的生存周期与产生该将亡值的函数的生命周期相同
int main
{
int a = 10;//a为左值,因其可寻址;反之10为右值
const int b = 20;//b为左值,其可寻址;
int& c = a; //正确
int& d = 10; //错误,左值无法引用右值
}
如何实现对常量的引用?
const int& a = 20;//1.使用常引用
//其底层实现如下: int tmp = 10;
// const int* const a = &tmp;
int&& b = 10;//2.使用右值引用
//其底层实现如下:
//int tmp = 10;
//int* const b = &tmp;
//在理解上,int&& b = 10 与int b = 10无异
*右值引用与将亡值
如图示,“右值引用”只可作为纯右值与将亡值的引用。
例1:
int main()
{
int a = 10;
int& b = a; //T
int&& c = a; //F, a为左值
int&& d = 10;//T,10是右值(字面常量)
int&& e = c;//F,c已具名,为左值
}
//编译错误,a具名可寻址,为左值,无法以右值引用返回
int&& fun()
{
int a = 10;
return a;
}
int main()
{
string& s1 = srting("test1");//F,左值引用无法引用不具名的对象(右值)
string&& s1 = srting("test1");//T
}
例2:
提高性能
int fun()
{
int a = 10;
return a;
}
int main()
{
int x = fun ();// T,fun()return时,a的值存入临时量,回到主函数时,临时量的值赋给x;
int& b = fun() ;// F,如上,函数在return时其值被存储于临时量(临时量不可寻址,为右值),因此不可将其作为左值的别名
const int& c = fun();//T,编译器判断接收值为常引用,会将a的值创建在主函数空间,并使c直接引用该空间,该值不可改
int&& d = fun () ; // T
//将亡值的创建同上,d的值可改
return 0;
上例中,d用右值引用获取将亡值,会延长其将亡值的生存周期,等同于d的生存周期
右值引用的应用
右值引用主要用以处理内置类型的将亡值
内置类型(char,int,double)与自设类型(class)处理方式不同,因此优化方式不同
struct类型的处理方式介于 内置 与 自设 之间
- class 构建的对象 = 方法 + 数据
- struct 构建的对象 = 数据 / 方法
- struct内只有数据类型时,其处理方法与内置类型相同
移动赋值与移动拷贝
见下例:
class Int
{
private:
int val;
public:
Int(){}
Int(int x) //构造函数重载
{
val= x;
}
Int(Int&& s)//移动拷贝
{
val = s.val;
}
Int& operator=(Int&& s) //移动构造
{
if(this!= &s )
val = s.val;
return *this;
}
};
Int fun()
{
Int s2(100);
return s2; //此处对临时对象的构建调用了移动拷贝函数
//注意,当要构建将亡值(即执行return语句)并且用户自建了移动拷贝构造函数时,s2可以合法赋值给 Int(Int&& s)中的s, 这是一种特例;在执行上,以上句为例,系统在执行return s2时,如果移动拷贝函数已被构建,则系统会将上句执行为return std::move(s2);这行语句的含义是将s2强转为右值(由此实现参数传递的合法性)
}
int main()
{
Int s1;
s1 = fun();
}
缺省的移动构造与移动赋值仅实现浅拷贝
右值引用与函数模板 – 何为完美转发
观察下例
void fun(int& a){...}
void fun(const int& a){...}
void fun(int&& a){...}
template<class T>
void fun(T&& a)
{
fun(a);
}
int main ()
{
int a = 10;
const int b = 20 ;
fun(a); //调用void fun(int& a)
fun(b); //调用void fun(const int& a)
fun(10); //调用void fun(const int& a)
}
完美转发
在上例中,我们看到,当调用函数模板时,
std::forword()函数用来保证参数的左右值属性在传递时不发生改变