c/c++ 右值引用 轉自:https://www.cnblogs.com/catch/p/3500678.html 左值(lvalue)和右值(rvalue)是 c/c++ 中一個比較晦澀基礎的概念,不少寫了很久c/c++的人甚至沒有聽過這個名字,但這個概念到了 c++11 後卻變得十分重要,它們 ...
c/c++ 右值引用
轉自:https://www.cnblogs.com/catch/p/3500678.html
左值(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&!