[C++] C++11新特性之左值右值、引用、左值引用右值引用、move、forward

参考

左值右值

C++的表达式要么是左值,要么是右值。

简单说,左值可以位于赋值语句的左边,右值则不能。

  • 右值:当一个对象被用作右值的时候,用的是对象的值(内容),右值要么是字面常量,要么在表达式求值过程中创建的临时对象,没有名字,不能被赋值。
  • 左值:当一个对象被用作左值的时候,用的是对象的身份(在内存中的位置),左值就是有名字的对象,可以被赋值,左值可以被取地址,左值持久
  • 需要右值的地方可以用左值来代替,不能把右值当成左值使用,当左值当成右值使用的时候,实际上使用的是它的内容(值)

需要用到左值

  • 赋值运算符需要一个(非常量)左值作为其左侧运算对象,得到的结果仍然是一个左值
  • 取地址符作用于一个左值运算对象,返回指向该运算对象的指针,这个指针是一个右值
  • 内置解引用运算符、下标运算符、迭代器解引用运算符、string和vector的下标运算符求值结果都是左值
  • 内置类型和迭代器的递增递减运算符作用于左值运算对象,前置版本作用的和得到的都是左值

什么是引用?

  • 说引用时,指的其实是左值引用&

  • 引用就是为对象起了另外一个名字,相当于起了一个绰号。通过将声明符写成&d来定义引用类型,其中d是声明的变量名

    int val = 11;//声明一个变量
    int &r = val;//正确:r是指向val的引用(r是val的一个别名)
    int &r1;//错误,引用必须初始化
    
  • 程序将引用和它的初始值绑定在一起,一旦初始化完成,引用将和它的初始值一直绑定在一起,无法令引用绑定到另一对象上

  • 为引用赋值时,实际上是把值赋给了引用绑定的对象,引用本身不是一个对象,所以不能定义引用的引用

  • 引用只能绑定在对象上,而不能绑定字面值或者某个表达式的计算结果

    int &r = 10;//错误,引用的初始值必须是一个对象
    
  • 特殊情况:const的引用

    int val = 11;
    const int &r1 = val;//正确
    const int &r2 = 12;//正确,常量引用
    const int &r3 = val * 2;//正确,常量引用
    int &r4 = val * 2;//错误
    

左值引用右值引用

#include <iostream>

//函数重载,形参为左值引用,接受左值
void process_value(int& i) { 
  std::cout << "LValue processed: " << i << std::endl; 
} 

//函数重载,形参为右值引用,接受右值
void process_value(int&& i) { 
  std::cout << "RValue processed: " << i << std::endl; 
} 

int main() { 
  int a = 0; 
  process_value(a);//传入左值,输出:LValue processed: 0
  process_value(1);//传入右值,输出1:RValue processed: 1
}
  • 左值引用&:不能将一个左值引用绑定到要求转换的表达式、字面常量或者返回右值的表达式

  • 右值引用&&:某个对象的另一个名字,可以将一个右值引用绑定到要求转换的表达式、字面常量或者返回右值的表达式,不能将右值引用直接绑定到一个左值上,右值引用指向将要被销毁的对象,用右值引用接管所引用的对象的资源

    int i = 1;
    int &&p = i;//错误,不能将一个右值引用绑定到一个左值上
    int &q = i * 22;//错误,不能将左值引用绑定到返回右值的表达式,i * 22是一个右值
    int &&q2 = i * 22;//正确,右值引用可以绑定到乘法右值结果上
    
  • 可以把左值引用绑定到返回左值的表达式(返回左值引用的函数、赋值、下标、解引用、前置递增递减运算符

    int i = 1;
    int &r = i;//正确,左值引用
    
  • 可以把const左值引用或者右值引用绑定到返回右值的表达式(返回非引用类型的函数、算术、关系、位、后置递增递减运算符

    const int &q1 = i * 22;//正确,可以将const引用绑定到右值
    
  • p是右值引用,指向一个右值,p本身是左值

    include <iostream>
    
    //函数重载,形参为左值引用,接受左值
    void process_value(int& i) { 
      std::cout << "LValue processed: " << i << std::endl; 
    } 
    
    //函数重载,形参为右值引用,接受右值
    void process_value(int&& i) { 
      std::cout << "RValue processed: "  << std::endl; 
    } 
    
    int main() { 
      int a = 0; 
      process_value(a);//输出:LValue processed: 0,说明传入的是左值
      int&& p = 3;
      process_value(p); //输出:LValue processed: 3,说明传入的是左值
    }
    //以上例子说明p是右值引用,指向一个右值,p本身是左值
    

    不能将一个右值引用绑定到一个右值引用类型的变量上

    变量是左值,不能将一个右值引用直接绑定到一个变量上

    int &&p = 11;//正确,字面常量是右值
    int &&q = p;//错误,p是左值
    
  • 右值引用的意义

    • 为临时变量续命,也就是为右值续命,因为右值在表达式结束后就消亡了,如果想继续使用右值,那就会动用昂贵的拷贝构造函数。

    • 右值引用是用来支持转移语义的。转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 的性能。

    • 转移语义是和拷贝语义相对的,可以类比文件的剪切与拷贝,当我们将文件从一个目录拷贝到另一个目录时,速度比剪切慢很多。

C++11 之move函数

头文件utility

使用move函数将获得一个绑定到左值上的右值引用类型

int &&p = 11;//正确,字面常量是右值
int &&q = p;//错误,p是左值
int &&q = std::move(p);//正确,move返回给定对象的右值引用

对于move不提供using声明,必须写成std::move,不能写成move

C++11 之forward

头文件utility

与move不同,forward必须通过显式模板实参来调用

forward返回该显式实参类型的右值引用,即forward返回的是T&&

template <typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2) {
    f(std::forward<T2>(t2), std::forward<T1>(t1));
}

//如果调用flip(g, i, 42),i将以int&类型传递给g,42将以int&&类型传递给g
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值