C++中的左值,右值,左值引用,右值引用

左值与右值(lvalue&rvalue

  左右值来源于C语言,原意是为了方便记忆:左值可以位于赋值语句的左侧,右值则不能。但是在C++中,左右值的区分没有那么简单,还存在 许多例外的情况,如:

  • 一个左值表达式的求值结果是一个对象或一个函数,然而以常量对象为代表的某些左值实际上是不能作为赋值语句的左侧运算对象的。如:
const int a =1;
//a是左值但不可修改
  • 某些表达式的求值结果是对象,但它们是右值而非左值。

  那么,到底如何区分左右值,我的理解是,一个左值表达式首先必须要能使用其地址,所以不能是常量表达式,计算式,字面常量。可以简单归纳为:

  • 左值:占用了一定内存,且拥有可辨认的地址的对象
  • 右值:左值以外的所有对象

  此外,左右值的使用可以总结出几点规律:

  • 一般而言,一个左值表达式表示的是一个对象的身份(在内存中的位置),而一个右值表达式表示的是一个对象的值(内容)
  • 在需要右值表达式的地方可以用左值表达式来代替(此时使用的是左值的值),但不能用右值表达式代替左值表达式。
  • 一个表达式一定是左值表达式和右值表达式中的一种。
  • 返回左值应用的函数,连同赋值、下标、解引用和前置递增/递减运算符,都生成左值。
  • 返回非引用类型的函数,连同算术、关系、位以及后置递增/递减运算符,都生成右值。

左值引用与右值引用(lvalue reference&rvalue reference)

 左值引用,就是正常的引用,需要注意的是const引用,

int i=1;
int &a = i;        //正确,i的左值引用
int &b =2;        //错误,左值引用不能绑定右值
const int &c = 3;    
// 正确,特殊情况,可以理解为临时创建了一个左值,然后引用

  右值引用,是C++11引入的新类型,所谓右值引用就是必须绑定到右值的引用。通过&&而不是&来获得右值引用。右值引用有一个重要的性质----只能绑定到一个将要销毁的对象。因此,可以自由地将一个右值引用的资源”移动”到另一个对象中。右值引用有两个主要用途:

  • 移动语意(Move Semantics)

  Rvalue引用支持移动语义的实现,这可以显着提高应用程序的性能。移动语义使您可以编写将资源(例如动态分配的内存)从一个对象转移到另一个对象的代码。移动语义之所以起作用,是因为它使资源能够从程序中其他地方无法引用的临时对象中转移。要实现移动语义,通常需要为类提供一个移动构造函数以及一个可选的移动赋值运算符(operator =)。源为右值的复制和赋值操作将自动利用移动语义。与默认的复制构造函数不同,编译器不提供默认的move构造函数。

  std::move就是一个将左值转化为右值的函数,例如:

 template<typename T>
void swap(T& a, T& b)
{
     T t(std::move(a));  // a为空,t占有a的初始数据
     a = std::move(b); //  b为空, a占有b的初始数据
     b = std::move(t); // t为空,b占有a的初始数据
} 
  • 完美转发(Perfect Forwarding)

  完美的转发减少了重载函数,避免了转发的问题。转发的问题出现在你写通用函数将引用作为参数,将这些参数由函数调用的时候。举个例子,如果通用函数将 type const T&作为参数,那么调用函数不能修改参数的值。如果通用函数将 type T&作为参数,那么当参数是右值的时候,函数不能调用。通常来说,为了解决上述的问题,你需要提供重载函数,既要有type const T&参数的函数,也要有type T&参数的函数。结果呢,重载函数的数量随着参数数量呈指数递增。而右值引用能够使你只用一个函数就能适用于任意数量的参数。

猜你喜欢

转载自www.cnblogs.com/honernan/p/12012259.html
今日推荐