C++之右值引用

C++之右值引用

c++11中的新概念,主要解决了移动语义和完美转发

我们平常使用的引用都是指左值引用。

下面是一个测试例子


void func ( int& i ) {
  std::cout << "func(int& i) = " << i << std::endl;
}

void func ( std::string& s ) {
  std::cout << "func(std::string& s) = " << s << std::endl;
}


void rvalue () {
  std::cout << std::endl << std::endl <<"------ rvalue test----" << std::endl;

  int i = 123;
  std::string s = "hello";

  func ( i );  // ok
  func ( s );  // ok

  func ( 1 );  // error : error C2665: “func”:2 个重载中没有一个可以转换所有参数类型
}

反正上面是出了问题,我们先了解一下基础知识在来回答上面的问题。
左值(lvalue)和右值(rvalue)是从c继承过来的概念,在c++11之后,新标准基于这两个概念新增了部分特征(右值引用,用来解决移动和转发语义)。
什么是左值、右值?
通俗的将:=左边的是左值,右边的是右值。
lvalue和rvalue的前缀怎么理解,left和right?好吧,l表示location,r表示read。
location表示可以在内存中寻址,可以被赋值,read表示可以直接知道值

什么是临时变量?
对于全局变量、局部变量,我们都清楚,那么什么是临时变量,什么时候出现。
怎么交换两个数?建一个临时变量和第一个交换,两个变量交换,临时变量和第二个交换。sorry,这只是我们业务逻辑上的临时变量,确切的说,只是我们创建的一个局部变量而已,那对于程序(或者说运行中的程序来说)什么才是临时变量?网上有位大神是这么解释的:编译期由编译器根据程序需要自动生成的,在运行期跑的时候是真实存在的,而主要发生在两个地方:函数传参发生类型转换时、函数返回值时。
比如说函数调用时,我们传一个字面量过去;或者函数返回一个字面量时;或是表达式中发生了类型转换时。

总结一句话:
1. 左值:能对表达式取地址、或具名对象/变量。一般指表达式结束后依然存在的持久对象。
2. 右值:不能对表达式取地址,或匿名对象。一般指表达式结束就不再存在的临时对象。

在c++中,临时对象不能作为左值,但可以作为常量引用const &
看下面的

++i = 3;  // ok
i++ = 3;  // error C2106: “=”: 左操作数必须为左值

++i:i先自增1,++i之后还是指向i的对象
i++:先把i自增之后的值丢给一个临时变量,自己去玩泥巴去了,i++之后指向的是那个临时变量。如果放在单条语句中,这两个没啥区别。

好了,现在看看上面出问题的地方func(1); 这个函数需要的是一个左值,但是字面量丢过去,她也只能放在一个临时变量中,而这个临时变量不能作为一个左值存在,就出了问题。

怎样解决?

void func ( int&& i ) 
{
  std::cout << "func(int&& i) = " << i << std::endl;
}   

或是用func ( const int& i )

扫描二维码关注公众号,回复: 1624282 查看本文章

&& 和 &一样都是引用,&&是新标准弄出来的,称为 右值引用。

无名右值引用

一般由static_cast < T&& >(t)转换操作转换而来
也可以用标准库提供的std::move()来将左值转换成右值引用

带名右值引用

T&& 这是一个左值,只不过她的类型是右值引用,只能绑定右值
额外说一句:如果类型是T&& 且这个T类型无需推导即可确定,那么这个变量称为带名右值引用;如果这个T类型需要推导,那么这个变量称为转发型引用。

总结一下:
1、新标准为c++带来了一个新的左值类型:带名右值引用;带来了一个新的右值类型:无名右值引用
2、左值引用(就是以前的引用)可以绑定左值,也可以绑定右值;右值引用只能绑定右值(新型右值类型(右值引用)、传统右值类型(临时对象))。

A a;
A&& b = static_cast< A&&>(a);
A&& c = std::move(a);
创建两个无名引用,用来初始化两个右值引用对象

A& d = a;
A& e = b;
左值引用可以绑定左值

const A& f = c;
const A& g = A();
const引用(若无特殊说明,引用二字都是指传统的左值引用)还可以绑定临时变量

A&& h = A();
右值引用也可以绑定临时变量

如果重载存在const引用和右值引用,新标准规定右值引用参数绑定的函数优先级高于const引用

void func (const int& i ) {
  std::cout << "func(int& i) = " << i << std::endl;
}

void func ( int&& i ) {
  std::cout << "func(int&& i) = " << i << std::endl;
}

void func ( std::string& s ) {
  std::cout << "func(std::string& s) = " << s << std::endl;
}


void rvalue () {
  std::cout << std::endl << std::endl << "----rvalue test------" << std::endl;

  int i = 123;
  std::string s = "hello";

  func ( i );  // ok
  func ( s );  // ok

//  ++i = 3;  // ok
//  i++ = 3;  // error C2106: “=”: 左操作数必须为左值

  func ( 1 );  // 传过去是一个右值,根据新标准规定调用void func ( int&& i )

  int&& a = std::move ( i );
  func ( a );  // 左值 别忘了a是左值,类型是右值引用
  func ( static_cast<int&&>( i ) );  // 右值
}

结果如下:
func(int& i) = 123
func(std::string& s) = hello
func(int&& i) = 1 // 虽然两个重载都满足,但是新标准规定右值引用绑定的优先级高于const左值引用
func(int& i) = 123
func(int&& i) = 123

原文出处

猜你喜欢

转载自blog.csdn.net/neverwa/article/details/80720008