C++ prvalue
C++17, prvalue被重新定义,我的思路都乱了。
左值(lvalue):具有id属性的值。能通过写代码的方式抓住的一个值。例如:
static int g_var=123; int& f(){ return g_var; } int main(){ f()=456; }
f()这个函数表达式的value catagories是lvalue。每次你写f()都能抓住相同的那个地址的值(存放在g_var)。
纯右值(prvalue):没有id属性的值,但是还能去move它。例如:
1 #include <iostream> 2 3 class S { 4 public: 5 6 S(int i) { 7 std::cout << "S(int i) called" << std::endl; 8 i_ = i; 9 if (i>0) 10 m_ = new char[i]; 11 } 12 13 ~S() { 14 if (m_) 15 delete[] m_; 16 } 17 18 S(S && other) { 19 std::cout << "S(S &&) called" << std::endl; 20 m_ = other.m_; 21 i_ = other.i_; 22 other.m_ = 0; 23 } 24 25 S(const S & other) = delete; 26 27 private: 28 char* m_=0; 29 int i_; 30 }; 31 32 S get() { 33 int x = 10; 34 std::cin >> x; 35 return S(x); 36 } 37 38 int main(){ 39 S s = get(); 40 return 0; 41 }
39行get返回一个纯右值,通过move constructor构造S s;与前面的f()不同的是,每次调用get()都是一个不同的值。这就是说,纯右值不具有id属性。你可以抢夺纯右值,抄它的家,这就是move属性。(注:在第20行开始,就是对纯右值的抄家过程)
但是可以通过S&& ref = get() 抓住临时对象,这延长了临时对象的生命周期。再次get()仍旧是另一个临时对象,与先前ref到的是不同的值。
x值(xvalue):同时具有id属性和move属性的值。这像极了蝙蝠,可以飞也有四肢,可以加入兽群,也能加入鸟类。(“X”像不像四肢和翅膀呢?)
S tmp(123); std::move(tmp);
std::move就是生成xvalue的神器,每次调用都会明确的指向tmp所在的地址(具有id性),但是可以传给move constructor,让别人偷窃tmp的财产。
C++17 pvalue的含义变化了,有时候pvalue是没有产生临时对象的,只是一个初始化。例如:
S get(){ return S{123}; } S s = get();
貌似有constructor和move constructor的调用,实际在“强制拷贝消除( Guaranteed Copy Elision ) 下,只有一个constructor的调用。依赖于move constructor副作用的人要大大的失望了(例如:希望cout出一条打印)。在编译器看来,S s = get(); 等价于 S s( S{123} ); 等价于S{123};
根据id,move属性分类值得思路,被彻底打乱了。期望新版的the C++ Programming language对这个问题做更简明的论述。没人期望像语言律师那样扣条文的方式学习C++。