表达式左值右值(C++学习)

来源:互联网 发布:淘宝网制作费用 编辑:程序博客网 时间:2024/06/05 01:14

左值右值是表达式的属性,该属性称为 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): lvaluefunc(const string& s): clvaluefunc(string&& s): rvaluefunc(const string&& s): crvalue

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

func(const string& s): lvaluefunc(const string& s): clvaluefunc(string&& s): rvaluefunc(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&) 1F1(const int&) 1F1(const int&) 2F1(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 2Destructor.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

PS:转自http://blog.csdn.net/dbzhang800/article/details/6663353
原创粉丝点击