c/c++ 右值引用

转自:https://www.cnblogs.com/catch/p/3500678.html

左值(lvalue)和右值(rvalue)是 c/c++ 中一个比较晦涩基础的概念,不少写了很久c/c++的人甚至没有听过这个名字,但这个概念到了 c++11 后却变得十分重要,它们是理解 move/forward 等新语义的基础。

左值右值的定义

左值与右值这两概念是从 c 中传承而来的,在 c 中,左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式).

int a;
int b; a = 3;
b = 4;
a = b;
b = a; // 以下写法不合法。
= a;
a+b = 4;

在 c 语言中,通常来说有名字的变量就是左值(如上面例子中的 a, b),而由运算操作(加减乘除,函数调用返回值等)所产生的中间结果(没有名字)就是右值,如上的 3 + 4, a + b 等。我们暂且可以认为:左值就是在程序中能够寻值的东西,右值就是没法取到它的地址的东西(不完全准确),但如上概念到了 c++ 中,就变得稍有不同。具体来说,在 c++ 中,每一个表达式都会产生一个左值,或者右值,相应的,该表达式也就被称作“左值表达式", "右值表达式"。对于基本数据类型来说(primitive types),左值右值的概念和 c 没有太多不同,不同的地方在于自定义的类型,而且这种不同比较容易让人混淆:

  1. 对于基础类型,右值是不可被修改的(non-modifiable),也不可被 const, volatile 所修饰(cv-qualitification ignored)

  2. 对于自定义的类型(user-defined types),右值却允许通过它的成员函数进行修改。

对于 1),这和 c 是一致的,2) 却是 C++ 中所独有, 因此,如果你看到 C++ 中如下的写法,千万不要惊讶:

#include <iostream>
using namespace std; class Test{
public:
Test(int d) : data(d){cout << "create:" << data << endl;}
~Test(){cout << "free:" << data << endl;} Test& operator = (const Test& other){
//data = other.data;
cout << "operator" << endl;
return *this;
} int get_data() const {return data;}
void set_data(int d){data = d;}
private:
int data;
}; Test getTest(){
int static i = 0;
return Test(i++);
} void func(Test& t){
cout << "func:" << t.get_data() << endl;
}
int main(){
//1 合法
(getTest() = Test(1)).set_data(12);
getTest() = Test(2);
getTest().set_data(20); //2 只能用const引用接收右值引用
const Test& ref = getTest();
//Test& ref1 = getTest();//error //3
//func(getTest());
func(getTest() = getTest());
}

这个特性看起来多少有些奇怪,因为通常来说,自定义类型应该设计得和内置类型尽量一样(所谓 value type,value semantic),但允许成员函数改变右值这个特性却有意无意使得自定义类型特殊化了。对此,我们其实### 可以这样想,也许会好理解点:

<font color="red"自定义类型允许有成员函数,而通过右值调用成员函数是被允许的,但成员函数有可能不是 const 类型,因此通过调用右值的成员函数,也就可能会修改了该右值,done!

左值引用,右值引用

关于右值,在 c++11 以前有一个十分值得关注的语言的特性:右值能被 const 类型的引用所指向,所以如下代码是合法的。

const Test& ref = getTest();

而且准确地说,右值只能被 const 类型的 reference 所指向,非 const 的引用则是非法的:

Test& ref1 = getTest();//error

当一个右值被 const 引用指向时,它的生命周期就被延长了。其中暗藏的逻辑其实就是:右值不能当成左值使用(但左值可以当成右值使用)。另外值得注意的是,对于前面提到的右值的两个特性:

  1. 允许调用成员函数。

  2. 只能被 const reference 指向。

它们导致了一些比较有意思的结果,比如:

void func(Test& c)
{
cout << "c:" << c.get_data() << endl;
} //error
func(getTest()); //正确
func(getTest() = getTest());

其中: func(get_cs() = get_cs()); 能够被正常编译执行的原因就在于,cs 的成员函数 operator=() 返回的是 cs&!

不允许非 const reference 引用 rvalue 并不是完美的。

c/c++ 右值引用的更多相关文章

  1. C++右值引用浅析

    一直想试着把自己理解和学习到的右值引用相关的技术细节整理并分享出来,希望能够对感兴趣的朋友提供帮助. 右值引用是C++11标准中新增的一个特性.右值引用允许程序员可以忽略逻辑上不需要的拷贝:而且还可以 ...

  2. C++ 11 中的右值引用

    C++ 11 中的右值引用 右值引用的功能 首先,我并不介绍什么是右值引用,而是以一个例子里来介绍一下右值引用的功能: #include <iostream>    #include &l ...

  3. 图说函数模板右值引用参数(T&&)类型推导规则(C++11)

    见下图: 规律总结: 只要我们传递一个基本类型是A④的左值,那么,传递后,T的类型就是A&,形参在函数体中的类型就是A&. 只要我们传递一个基本类型是A的右值,那么,传递后,T的类型就 ...

  4. c++11的右值引用、移动语义

    对于c++11来说移动语义是一个重要的概念,一直以来我对这个概念都似懂非懂.最近翻翻资料感觉突然开窍,因此记下.其实搞懂之后就会发现这个概念很简单,并无什么高深的地方. 先说说右值引用.右值一般指的是 ...

  5. VS2012 error C2664: “std::make_pair”:无法将左值绑定到右值引用

    在vs2012(c++)make_pair()改动: C++: template <class T1, class T2> pair<V1, V2> make_pair(T1& ...

  6. 右值引用、move与move constructor

    http://blog.chinaunix.net/uid-20726254-id-3486721.htm 这个绝对是新增的top特性,篇幅非常多.看着就有点费劲,总结更费劲. 原来的标准当中,参数与 ...

  7. 【转】C++11 标准新特性: 右值引用与转移语义

    VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...

  8. move语义和右值引用

    C++11支持move语义,用以避免非必要拷贝和临时对象. 具体内容见收藏中的“C++右值引用” .

  9. [转载] C++11中的右值引用

    C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...

  10. C++ 11 右值引用

    C++11中引入的一个非常重要的概念就是右值引用.理解右值引用是学习“移动语义”(move semantics)的基础.而要理解右值引用,就必须先区分左值与右值. 注意:左值右值翻译可能有些问题 *L ...

随机推荐

  1. 【原创+整理】简述何为调用约定,函数导出名以及extern C

    何为调用约定 调用约定指的是函数在调用时会按照不同规则,翻译成不同的汇编代码.这和参数的压栈顺序和栈的清理方式相关,也就是说不同的调用约定,这些方式会做相应改变.一般编译器是以默认的调用约定编译一份代 ...

  2. Java中的instanceof和isInstance基础讲解

    1. instanceof 是一个操作符 使用方法: ? 1 2 if(a instanceof B){ } 表示:a 是不是 B 这种类型 2. isInstance是Class类的一个方法 ? 1 ...

  3. 网络爬虫之html2md

    前言 上周利用java爬取的网络文章,一直未能利用java实现html转化md,整整一周时间才得以解决. 虽然本人的博客文章数量不多,但是绝不齿于手动转换,毕竟手动转换浪费时间,把那些时间用来做些别的 ...

  4. Hystrix降级逻辑中如何获取触发的异常

    通过之前Spring Cloud系列教程中的<Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)>一文,我们已经知道如何通过Hystrix来保护自己的服务不被外 ...

  5. 怎样监听vue.js中v-for全部渲染完成?

    vue里面本身带有两个回调函数: 一个是Vue.nextTick(callback),当数据发生变化,更新后执行回调. 另一个是Vue.$nextTick(callback),当dom发生变化,更新后 ...

  6. 快速搭建一个Quartz定时任务【转载,好文 ,值得收藏,亲身试用 效果不错】

    Quartz.NET 入门 概述 Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等. Quartz.NET允许开发人员根据时间间隔 ...

  7. 利用http协议对搜索引擎劫持

    主要是利用了http协议的refereer头 另外一个头user-agnet 主要是用来做流量劫持 referer 头告诉服务器用户从哪里找来的 当用户通过搜索引擎打开网站时会出现源网页 refere ...

  8. cmd Telnet 手工模拟http请求

    telnet Windows系统自带的访问页面命令 首先打开控制面板点击卸载程序 打开windows功能 勾选 telnt 客户端 在打开cmd 命令 输入 telnt www.baidu.com 8 ...

  9. EF Codefirst 中间表(关系表)的增删改查(转)

    EF Codefirst 多对多关系 操作中间表的 增删改查(CRUD)   前言 此文章只是为了给新手程序员,和经验不多的程序员,在学习ef和lambada表达式的过程中可能遇到的问题. 本次使用订 ...

  10. Redirection

    Typically, the syntax of these characters is as follows, using < to redirect input, and > to r ...