左值右值是表达式的属性,该属性称为 value category。按该属性分类,每一个表达式属于下列之一:

lvalue

left value,传统意义上的左值

xvalue

expiring value, x值,指通过“右值引用”产生的对象

prvalue

pure rvalue,纯右值,传统意义上的右值(?)

而 xvalue 和其他两个类型分别复合,构成:

lvalue + xvalue = glvalue

general lvalue,泛左值

xvalue + prvalue = rvalue

右值

区分?

++x 与 x++ 假定x的定义为 int x=0;,那么前者是 lvalue,后者是rvalue。前者修改自身值,并返回自身;后者先创建一个临时对像,为其赋值,而后修改x的值,最后返回临时对像。

区分表达式的左右值属性有一个简便方法:若可对表达式用 & 符取址,则为左值,否则为右值。比如

&obj , &*ptr , &ptr[index] , &++x

有效

&1729 , &(x + y) , &std::string("meow"), &x++

无效

对于函数调用,根绝返回值类型不同,可以是lvalue、xvalue、prvalue:

  • The result of calling a function whose return type is an lvalue reference is an lvalue

  • The result of calling a function whose return type is an rvalue reference is an xvalue.

  • The result of calling a function whose return type is not a reference is a prvalue.

const vs non-const

左值和右值表达式都可以是const或non-const。

比如,变量和函数的定义为:

string one("lvalue");
const string two("clvalue");
string three() { return "rvalue"; }
const string four() { return "crvalue"; }

那么表达式:

表达式

分类

one

modifiable lvalue

two

const lvalue

three()

modifiable rvalue

four()

const rvalue

引用

Type&

只能绑定到可修改的左值表达式

const Type&

可以绑定到任何表达式

Type&&

可绑定到可修改的左值或右值表达式

const Type&&

可以绑定到任何表达式

重载函数

#include <iostream>
#include <string>
using namespace std;

string one("lvalue");
const string two("clvalue");
string three() { return "rvalue"; }
const string four() { return "crvalue"; }

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

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

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

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

int main()
{
    func(one);
    func(two);
    func(three());
    func(four());
    return 0;
}

结果:

func(string& s): lvalue
func(const string& s): clvalue
func(string&& s): rvalue
func(const string&& s): crvalue

如果只保留const string& 和 string&& 两个重载函数,结果为:

func(const string& s): lvalue
func(const string& s): clvalue
func(string&& s): rvalue
func(const string& s): crvalue

右值引用

C++0x第5章的第6段:

Named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.
  • 具名右值引用被视为左值
  • 无名对对象的右值引用被视为x值
  • 对函数的右值引用无论具名与否都将被视为左值
#include <iostream>
#include <string>

void F1(int&& a)
{
    std::cout<<"F1(int&&) "<<a<<std::endl;
}

void F1(const int& a)
{
    std::cout<<"F1(const int&) "<<a<<std::endl;
}

void F2(int&& a)
{
    F1(a);
}

int main()
{
    int && a=1;
    F2(a);
    F1(a);
    F2(2);
    F1(2);
    return 0;
}

结果

F1(const int&) 1
F1(const int&) 1
F1(const int&) 2
F1(int&&) 2

移动语义

在这之前,如果写一个交换两个值的swap函数:

template <class T> swap(T& a, T& b)
{
    T tmp(a);   // now we have two copies of a
    a = b;      // now we have two copies of b
    b = tmp;    // now we have two copies of tmp (aka a)
}

之后

template <class T> swap(T& a, T& b)
{
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

std::move 接受左值或右值参数,并返回一个右值(其所作工作很简单)

template <class T>
typename remove_reference<T>::type&&
move(T&& a)
{
    return a;
}

要是的swap真正发挥作用,需要重载:

class T
{
public:
    T(T&& );
    T& operator = (T&& );
...

模板参数类型

为了对比左值引用和右值引用,一开始误打误撞,写了这样一个函数

template <typename Type> void Swap(Type&& sb1, Type&& sb2)
{
    Type sb(sb1);
    sb1 = sb2;
    sb2 = sb;
}

然后

int main()
{
    int a=1, b=2;
    Swap(a, b);
    std::cout<<a<<" "<<b<<std::endl;
    return 0;
}

结果却是

2 2

不用整数,换用一个自定义的类型试试看:

class A
{
public:
    A() {
        std::cout << "Default constructor." << std::endl;
        m_p = NULL;
    }

    ~A() {
        std::cout << "Destructor." << std::endl;
        delete m_p;
    }

    explicit A(const int n) {
        std::cout << "Unary constructor." << std::endl;
        m_p = new int(n);
    }

    A(const A& other) {
        std::cout << "Copy constructor." << std::endl;
        if (other.m_p) {
            m_p = new int(*other.m_p);
        } else {
            m_p = NULL;
        }
    }

    A(A&& other) {
        std::cout << "Move constructor." << std::endl;
        m_p = other.m_p;
        other.m_p = NULL;
    }

    A& operator=(const A& other) {
        std::cout << "Copy assignment operator." << std::endl;
        if (this != &other) {
            delete m_p;
            if (other.m_p) {
                m_p = new int(*other.m_p);
            } else {
                m_p = NULL;
            }
        }
        return *this;
    }

    A& operator=(A&& other) {
        std::cout << "Move assignment operator." << std::endl;
        if (this != &other) {
            delete m_p;
            m_p = other.m_p;
            other.m_p = NULL;
        }
        return *this;
    }

    int get() const {
        return m_p ? *m_p : 0;
    }

private:
    int * m_p;
};

int main()
{
    A a(1);
    A b(2);
    Swap2(a, b);
    std::cout<<a.get()<<" "<<b.get()<<std::endl;
    return 0;
}

结果

Unary constructor.
Unary constructor.
Copy assignment operator.
Copy assignment operator.
2 2
Destructor.
Destructor.

只出现了两个对象,那么Swap中的临时对象去哪儿了?

C++0x 14.8.2.1

If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cv unqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction. 

template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue

也就是前面提到的

template <typename Type> void Swap(Type&& sb1, Type&& sb2)

参数推导后

void Swap<int&>(int& sb1, int& sb1)

参考

  • http://blog.csdn.net/zwvista/article/details/5459774

  • http://blog.csdn.net/hikaliv/article/details/4541429

  • http://topic.csdn.net/u/20090706/16/514af7e1-ad20-4ea3-bdf0-bfe6d34d9814.html

  • http://www.artima.com/cppsource/rvalue.html

表达式左值右值(C++学习)相关推荐

  1. C++基础知识(二)--左值右值--逻辑表达式求值优化--逗号运算符与表示式--输入输出格式控制...

    :一.C++左值右值概念 左值:c++将变量名代表的单元称为左值,而将变量的值称为右值,左值必须是内存中可以访问且可以合法修改的对象,因此只能是变量名,而不能是常量或表达式.即左值可以寻址. 右值:将 ...

  2. C、C++差异之左值右值

    C与C++在语法细节上还是有一些差异的,虽然一般情况下可能这些差异不足以造成结果的区别,但有些代码确实会有影响. 这次,主要总结下左值右值的差异. 在C中,很多左值运算符的结果都不再是左值,然而在C+ ...

  3. C++11新特性之左值右值及移动语句与完美转发

    C左值右值 左值和右值的由来 什么是左值和右值 左值右值的本质 引用 左值引用 右值引用 移动语句与完美转发 移动语句 实现移动构造函数和转移赋值函数 stdmove 完美转发Perfect Forw ...

  4. c++ 左值 广义左值 右值 纯右值 将亡值

    为什么C/C++等少数编程语言要区分左右值? 历史原因: C语言作为一门古老的编程语言,其设计初衷是为了在硬件资源有限的系统上进行高效的编程,因此其语法和语义设计相对较简单.左值和右值的概念最初是由C ...

  5. 左值右值将亡值泛左值

    左右值概念 简单理解 左值:赋值运算符左边的变量,可以接受右边值,例如 int a = 10; a就是一个左值右值:赋值运算符右边的值,这个值可以是一个变量也可以是一个常量,例如 int a = 10 ...

  6. 左值/右值/左值引用/右值引用/move的用法介绍

    目录 问题 左值和右值 概念总结: 需要用到左值的运算符: 引用分类 左值引用 右值引用 右值引用到底什么用? std::move()函数介绍 问题 什么是左值和右值? 什么是左/右值引用? 左/右值 ...

  7. std::move 左值右值 左值引用右值引用

    参考:https://blog.csdn.net/daaikuaichuan/article/details/88371948 https://zhuanlan.zhihu.com/p/9458820 ...

  8. java中的左值右值_快速了解C/C++的左值和右值

    最近在segmentfault上看到一个提问<c++隐式的类类型转换问题>:一时不知怎么回答,查阅相关资料后整理了本文,以供参考学习. 定义 早期的C给出的定义:左值是一个表达式,可能出现 ...

  9. C++易被忽略的知识点:移动语义 左值右值

    目录 lvalue 和 rvalue rvalue 引用 移动语义 移动语义的概念 强制移动 lvalue 和 rvalue 每个表达式都会得到 lvalue 或 rvalue.它们的区别是,lval ...

最新文章

  1. 在vscode中统一vue编码风格的方法
  2. 百度的云智一体,让视频变得“硬核性感”
  3. 用matlab求解不等方程组,Matlab:求高人指点用matlab求解非线性方程组,解出来的值不收敛,提前结束...
  4. FI模块组织机构配置文档
  5. 步骤6 - WebSocket服务器把请求的响应结果推送给webshop
  6. 软件项目组织管理(一)项目管理概述
  7. matlab 安装jdbc.jar
  8. 加速 SpringBoot 应用开发,官方热部署神器真带劲!
  9. mbot机器人自动超声波模式程序_垃圾分类管家小程序 垃圾自动分类机器人,垃圾分类助手垃圾分类系统学习 AI自动垃圾分类识别...
  10. Excel导入导出组件的设计
  11. python学习之多线程多进程
  12. 3D物理引擎JiglibFlash
  13. c#读取文本文件出现乱码
  14. Vercel部署个人博客
  15. 安装系统时一直是程序正在启动服务器,全新安装win10卡在安装程序正在启动该怎么办?...
  16. uniapp+nvue开发之仿微信语音+视频通话功能 :实现一对一语音视频在线通话
  17. BIGEMAP谷歌卫星地图下载器
  18. 服务器域共享文件夹,访问域共享文件夹
  19. 键盘上哪个键是ESCAPE键?
  20. 微波技术大作业课设-分立电容电感+微带单枝短截线+微带双枝短截线

热门文章

  1. 突破百度硬盘限制速度的傻瓜教程(非迅雷),20210630亲测可用
  2. python爬取喜马拉雅有声小说
  3. 华南理工大学软件学院2016考研复试机试第一题代码(Java)
  4. 关于Python的书籍的阅读心得,推荐一本python的书籍
  5. 多项式算法6:多项式快速幂
  6. 电子通信领域经典书籍推荐
  7. oppoA83怎么升级android版本,OPPO A83 刷机教程 OPPO A83 卡刷升级教程
  8. 汽车环境感知技术详解【 持续更新ing 】— 超声波传感器原理及应用
  9. Java程序控制结构、顺序控制、循环控制及跳转控制语句
  10. java特粗宋体_Java IdentityPlusMapper类代码示例