zoukankan      html  css  js  c++  java
  • 表达式左值右值

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

    lvalue

    left value,传统意义上的左值

    xvalue

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

    prvalue

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

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

    lvalue + xvalue = glvalue

    general lvalue,泛左值

    xvalue + prvalue = rvalue

    右值

    1.区分

      ++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.

    2.const vs non-const

      左值右值表达式都可以是constnon-const。比如,变量和函数的定义为:

    1 string one("lvalue");
    2 const string two("clvalue");
    3 string three() { return "rvalue"; }
    4 const string four() { return "crvalue"; }
    View Code

      那么表达式:

    表达式

    分类

    one

    modifiable lvalue

    two

    const lvalue

    three()

    modifiable rvalue

    four()

    const rvalue

      引用

    Type&

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

    const Type&

    可以绑定到任何表达式

    Type&&

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

    const Type&&

    可以绑定到任何表达式

    3.重载函数

     1 #include <iostream>
     2 #include <string>
     3 using namespace std;
     4 
     5 string one("lvalue");
     6 const string two("clvalue");
     7 string three() { return "rvalue"; }
     8 const string four() { return "crvalue"; }
     9 
    10 void func(string& s)
    11 {
    12     cout << "func(string& s): " << s << endl;
    13 }
    14 
    15 void func(const string& s)
    16 {
    17     cout << "func(const string& s): " << s << endl;
    18 }
    19 
    20 void func(string&& s)
    21 {
    22     cout << "func(string&& s): " << s << endl;
    23 }
    24 
    25 void func(const string&& s)
    26 {
    27     cout << "func(const string&& s): " << s << endl;
    28 }
    29 
    30 int main()
    31 {
    32     func(one);
    33     func(two);
    34     func(three());
    35     func(four());
    36     return 0;
    37 }
    View Code

      结果:

    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

    4.右值引用

      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值
    • 对函数的右值引用无论具名与否都将被视为左值
     1 #include <iostream>
     2 #include <string>
     3 
     4 void F1(int&& a)
     5 {
     6     std::cout<<"F1(int&&) "<<a<<std::endl;
     7 }
     8 
     9 void F1(const int& a)
    10 {
    11     std::cout<<"F1(const int&) "<<a<<std::endl;
    12 }
    13 
    14 void F2(int&& a)
    15 {
    16     F1(a);
    17 }
    18 
    19 int main()
    20 {
    21     int && a=1;
    22     F2(a);
    23     F1(a);
    24     F2(2);
    25     F1(2);
    26     return 0;
    27 }
    View Code

      结果

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

    5.移动语义

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

    1 template <class T> swap(T& a, T& b)
    2 {
    3     T tmp(a);   // now we have two copies of a
    4     a = b;      // now we have two copies of b
    5     b = tmp;    // now we have two copies of tmp     
    6 }
    View Code

      之后

    1 template <class T> swap(T& a, T& b)
    2 {
    3     T tmp(std::move(a));
    4     a = std::move(b);   
    5     b = std::move(tmp);
    6 }
    View Code

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

    1 template <class T>
    2 typename remove_reference<T>::type&&
    3 move(T&& a)
    4 {
    5     return a;
    6 }
    View Code

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

    1 class T
    2 {
    3 public:
    4     T(T&& );
    5     T& operator = (T&& );
    6 ...
    View Code

    6.模板参数类型

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

    1 template <typename Type> void Swap(Type&& sb1, Type&& sb2)
    2 {
    3     Type sb(sb1);
    4     sb1 = sb2;
    5     sb2 = sb;
    6 }
    View Code

      然后

    1 int main()
    2 {
    3     int a=1, b=2;
    4     Swap(a, b);
    5     std::cout<<a<<" "<<b<<std::endl;
    6     return 0;
    7 }
    View Code

      结果却是

    2 2

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

     1 class A 
     2 {
     3 public:
     4     A() {
     5         std::cout << "Default constructor." << std::endl;
     6         m_p = NULL;
     7     }
     8 
     9     ~A() {
    10         std::cout << "Destructor." << std::endl;
    11         delete m_p;
    12     }
    13 
    14     explicit A(const int n) {
    15         std::cout << "Unary constructor." << std::endl;
    16         m_p = new int(n);
    17     }
    18 
    19     A(const A& other) {
    20         std::cout << "Copy constructor." << std::endl;
    21         if (other.m_p) {
    22             m_p = new int(*other.m_p);
    23         } else {
    24             m_p = NULL;
    25         }
    26     }
    27 
    28     A(A&& other) {
    29         std::cout << "Move constructor." << std::endl;
    30         m_p = other.m_p;
    31         other.m_p = NULL;
    32     }
    33 
    34     A& operator=(const A& other) {
    35         std::cout << "Copy assignment operator." << std::endl;
    36         if (this != &other) {
    37             delete m_p;
    38             if (other.m_p) {
    39                 m_p = new int(*other.m_p);
    40             } else {
    41                 m_p = NULL;
    42             }
    43         }
    44         return *this;
    45     }
    46 
    47     A& operator=(A&& other) {
    48         std::cout << "Move assignment operator." << std::endl;
    49         if (this != &other) {
    50             delete m_p;
    51             m_p = other.m_p;
    52             other.m_p = NULL;
    53         }
    54         return *this;
    55     }
    56 
    57     int get() const {
    58         return m_p ? *m_p : 0;
    59     }
    60 
    61 private:
    62     int * m_p;
    63 };
    64 
    65 int main()
    66 {
    67     A a(1);
    68     A b(2);
    69     Swap2(a, b);
    70     std::cout<<a.get()<<" "<<b.get()<<std::endl;
    71     return 0;
    72 }
    View Code

      结果

    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. 
    1 template <class T> int f(T&&);
    2 template <class T> int g(const T&&);
    3 int i;
    4 int n1 = f(i); // calls f<int&>(int&)
    5 int n2 = f(0); // calls f<int>(int&&)
    6 int n3 = g(i); // error: would call g<int>(const int&&), which
    7 // would bind an rvalue reference to an lvalue
    View Code
     也就是前面提到的
    1 template <typename Type> void Swap(Type&& sb1, Type&& sb2)

      参数推导后

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

    7.参考

  • 相关阅读:
    c++ string 的注意事项
    vim 高级技巧
    常用工具
    网络安全测试工具
    RMQ ST算法
    高精度模板
    CodeForces
    CodeForces
    线段树初探
    树状数组初探
  • 原文地址:https://www.cnblogs.com/blueoverflow/p/4817395.html
Copyright © 2011-2022 走看看