c/c++ 右值引用
c/c++ 右值引用
转自:
左值(lvalue)和右值(rvalue)是 c/c++ 中一个比较晦涩基础的概念,不少写了很久c/c++的人甚至没有听过这个名字,但这个概念到了 c++11 后却变得十分重要,它们是理解 move/forward 等新语义的基础。
左值右值的定义
左值与右值这两概念是从 c 中传承而来的,在 c 中,左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式).
int a; int b; a = 3; b = 4; a = b; b = a; // 以下写法不合法。 = a; a+b = 4;
在 c 语言中,通常来说有名字的变量就是左值(如上面例子中的 a, b),而由运算操作(加减乘除,函数调用返回值等)所产生的中间结果(没有名字)就是右值,如上的 3 + 4, a + b 等。我们暂且可以认为:左值就是在程序中能够寻值的东西,右值就是没法取到它的地址的东西(不完全准确),但如上概念到了 c++ 中,就变得稍有不同。具体来说,在 c++ 中,每一个表达式都会产生一个左值,或者右值,相应的,该表达式也就被称作“左值表达式", "右值表达式"。对于基本数据类型来说(primitive types),左值右值的概念和 c 没有太多不同,不同的地方在于自定义的类型,而且这种不同比较容易让人混淆:
1) 对于基础类型,右值是不可被修改的(non-modifiable),也不可被 const, volatile 所修饰(cv-qualitification ignored)
2) 对于自定义的类型(user-defined types),右值却允许通过它的成员函数进行修改。
对于 1),这和 c 是一致的,2) 却是 c++ 中所独有, 因此,如果你看到 c++ 中如下的写法,千万不要惊讶:
#include <iostream> using namespace std; class test{ public: test(int d) : data(d){cout << "create:" << data << endl;} ~test(){cout << "free:" << data << endl;} test& operator = (const test& other){ //data = other.data; cout << "operator" << endl; return *this; } int get_data() const {return data;} void set_data(int d){data = d;} private: int data; }; test gettest(){ int static i = 0; return test(i++); } void func(test& t){ cout << "func:" << t.get_data() << endl; } int main(){ //1 合法 (gettest() = test(1)).set_data(12); gettest() = test(2); gettest().set_data(20); //2 只能用const引用接收右值引用 const test& ref = gettest(); //test& ref1 = gettest();//error //3 //func(gettest()); func(gettest() = gettest()); }
这个特性看起来多少有些奇怪,因为通常来说,自定义类型应该设计得和内置类型尽量一样(所谓 value type,value semantic),但允许成员函数改变右值这个特性却有意无意使得自定义类型特殊化了。对此,我们其实### 可以这样想,也许会好理解点:
<font color="red"自定义类型允许有成员函数,而通过右值调用成员函数是被允许的,但成员函数有可能不是 const 类型,因此通过调用右值的成员函数,也就可能会修改了该右值,done!
左值引用,右值引用
关于右值,在 c++11 以前有一个十分值得关注的语言的特性:右值能被 const 类型的引用所指向,所以如下代码是合法的。
const test& ref = gettest();
而且准确地说,右值只能被 const 类型的 reference 所指向,非 const 的引用则是非法的:
test& ref1 = gettest();//error
当一个右值被 const 引用指向时,它的生命周期就被延长了。其中暗藏的逻辑其实就是:右值不能当成左值使用(但左值可以当成右值使用)。另外值得注意的是,对于前面提到的右值的两个特性:
1) 允许调用成员函数。
2) 只能被 const reference 指向。
它们导致了一些比较有意思的结果,比如:
void func(test& c) { cout << "c:" << c.get_data() << endl; } //error func(gettest()); //正确 func(gettest() = gettest());
其中: func(get_cs() = get_cs()); 能够被正常编译执行的原因就在于,cs 的成员函数 operator=() 返回的是 cs&!
不允许非 const reference 引用 rvalue 并不是完美的。
上一篇: .gitignore
下一篇: python小技巧