lvalues and rvalues

来源:互联网 发布:淘宝中心网页版登录 编辑:程序博客网 时间:2024/05/15 12:48

参考链接:https://msdn.microsoft.com/en-us/library/f90831hc.aspx

  1. C++的任何表达式都是lvalue或者rvalue。lvalue指的是 在表达式之外依然一致的object,可以认为拥有名字的object都是lvalue,所以所有的变量都是lvalue。rvalue指的是 临时的值,在表达式之外使用就无法保证一致。

    // lvalues_and_rvalues.cpp  int main()  {  int i, j, *p;  // Correct usage: the variable i is an lvalue.  i = 7;  // Incorrect usage: The left operand must be an lvalue (C2106).  7 = i; // C2106  j * 4 = 7; // C2106, 如果重载乘号返回的是一个左值,这里也可以是正确的// Correct usage: the dereferenced pointer is an lvalue.  *p = i;   const int ci = 7;  // Incorrect usage: the variable is a non-modifiable lvalue (C3892).  ci = 9; // C3892  // Correct usage: the conditional operator returns an lvalue.  ((i < 3) ? i : j) = 7;  }  

    ​ 欲详细了解rvalue、lvalue、xvalue、glvalue、prvalue 见stackoverflow

  2. lvalue reference:可以认为左值引用就是另一个object的别名。用法为type-id & cast-expression

  3. rvalue reference:用法为type-id && cast-expression

    • move semantics

      右值引用是一个作用为了支持move semantics :允许无法引用的临时对象资源被传递。(move senmatics的实现可以参照这里。)一个例子是string的operator+。在VC++ 2010以前,operator+并不能用于直接将两个string相加。因为如果两个string都是lvalue,则很有可能在其他地方被引用了,因此不能更改它们。而rvalue不能在程序其它地方引用,operator+就不用害怕意外改变了其它地方的值,因此可以将两个string相加。同样还有vector溢出处理的问题可以帮助理解,详细看这里。

    int main()  {    string s = string("h") + "e" + "ll" + "o";    cout << s << endl;  }  
    • perfect forwarding

      这里的forward的意思是一个泛型函数(比如模板函数)参数是引用参数,并将这些参数传递(forward)给其它函数。比如泛函数参数类型为const T&,则被调用的函数不能修改参数值。(而lvalue是允许修改的 )如果类型是T&,则函数不能传入rvalue调用(因为rvalue不能在其他修改,而T&允许修改参数 )。

      ​ 比如针对如下类,要写一个调用他们的泛函数。

      struct W  {  W(int&, int&) {}  };  struct X  {  X(const int&, int&) {}  };  struct Y  {  Y(int&, const int&) {}  };  struct Z  {  Z(const int&, const int&) {}  };  

      这是一个版本,但他只能调用左值,也就是允许在其他地方修改的值,要想代码正确需要一一实现对应的重载函数。

      template <typename T, typename A1, typename A2>  T* factory(A1& a1, A2& a2)  {  return new T(a1, a2);  }  int a = 4, b = 5;  W* pw = factory<W>(a, b);  Z* pz = factory<Z>(2, 2); // ERROR, rvalue, factory can only take lvalue reference

      而rvalue reference允许只用下面一个泛函数解决问题:

      template <typename T, typename A1, typename A2>  T* factory(A1&& a1, A2&& a2)  {  return new T(std::forward<A1>(a1), std::forward<A2>(a2));  }  // std::forward的作用是将factory函数的参数传给模板类的构造函数int main()  {  int a = 4, b = 5;  W* pw = factory<W>(a, b);  X* px = factory<X>(2, b);  Y* py = factory<Y>(a, 2);  Z* pz = factory<Z>(2, 2);  }  

      允许facotry同时进行lvalue和rvalue调用的原因是由于reference collapsing rule的存在,可以对推测参数类型。

      Template argument deduction is an important element of implementing perfect forwarding.

      collapsing rule :

    Expanded type Collapsed type T& & T& T& && T& T&& & T&& T&& && T&&

    比如以下例子:

    c++
    template<typename T>
    void foo(T&&);

    1. 当foo传进参数A类型是lvalue, 则编译器将T推测为A&, 根据collapsing rule, A& &&就是A&。
    2. foo传进参数A类型是rvalue, 则T被推测为A, 因此A&&就是A&&。

      更详细的,可参考stackoverflow上的解释。

      还是没非常理解,需要多看看相关知识。。@_@

原创粉丝点击