前言
為了了解C++11的新特性右值引用,不得不重新認識一下左右值。學習之初,最快的理解,莫過於望文生義了,右值那就是賦值號右邊的值,左值就是賦值號左邊的值。在中學的數學的學習中,我們理解的是,左值等價於等號左邊的值,右值等價於等號右邊的值;當我們繼續學習C語言時,等號=不再叫等號,蓋頭換面叫做賦值號;那么來到C++我們還能這么理解嗎?答案是部分否定的。
假如你現在還是這樣理解,那么請繼續往下......
C++中何為左值lvalue和右值rvalue?
左值lvalue:可被引用的數據對象,例如,變量、數組元素、結構成員、引用和解除引用的指針都是左值。在C語言中,左值最初指的是出現在賦值語句左邊的實體,但這是引入const之前的情況。now,常規變量和const變量都可視為左值,因為可通過地址訪問它們。常規變量屬於可修改的左值,const變量屬於不可修改的左值。左值基本上和以前的認知沒有太大的改變。
右值rvalue:字面常量(用括號括起來的字符串除外,因為它們表示地址)、包含多項的表達式以及返回值的函數(條件是該函數返回的不是引用)。
簡單的區別左值和右值:
右值就是一個臨時變量(后面將詳細的解釋),只有臨時地址空間,左值有其地址空間,換句話說,使用取地址符&對某個值取地址,如果不能得到地址,則是右值!
例如:
int a = 0;
cout << &a << endl; // ok! lvalue
cout << &404; // error! rvalue
對於前面提到的右值的特性:
1) 允許調用成員函數。
2) 只能被 const reference 指向。
3) 右值不能當成左值使用,但左值可以當成右值使用
臨時變量
僅當函數參數為const reference時,臨時變量才能與之匹配。
什么時候將創建臨時變量呢?
如果引用參數是const,則編譯器將在下面兩種情況下生成臨時變量:
1. 實參的類型正確,但不是左值
2. 實參的類型不正確,但可以轉換為正確的類型
Example:
double cube(const double &ra) { return ra * ra * ra; } double side = 3.0; double* pd = &side; double& rd = side; long edge = 5L; double lens[5] = {2.0, 5.0, 10.0, 12.0}; double c1 = cube(side); double c2 = cube(lens[2]); double c3 = cube(rd); double c4 = cube(*pd); double c5 = cube(edge); // ra是臨時變量 double c6 = cube(7.0); // ra是臨時變量 double c7 = cube(side + 4.0); // ra是臨時變量
參數side、lens[2]、rd和*pd都是有名稱的、double類型的數據對象,因此不需要借助臨時變量,可以為其創建引用。
然而edge雖然有名稱但是類型卻不正確,正好符合產生臨時變量的情況2 “實參的類型不正確,但可以轉換為正確的類型” ,double引用不能指向long。參數7.0和side + 4.0的類型都正確,但是木有名稱,屬於右值。在這些情況下,編譯器都會生成一個臨時匿名變量,並讓ra指向它。這些變量生命周期只在函數調用期間,此后編譯器便會隨意的刪除。
為何臨時變量 or 右值 對const引用的限制時合理的呢?
請看下面的例子:
void swapr(int& a, int& b) // swapr()完成數的交換的功能 { int temp; temp = a; a = b; b = temp; } long a = 5, b = 6; swapr(a, b);
這里我們使用反證法,假設這個調用是木有錯誤的,那么這里的類型不匹配,因此編譯器將創建兩個int臨時變量,將它們初始化為3和5,然后交換臨時變量的內容,但是這只是交換了兩個臨時變量的值,a和 b並沒有交換,這與swapr()函數完成的功能是相悖的。
總結來說,如果接受引用參數的函數的意圖是修改作為參數傳遞的變量,則創建臨時變量將阻止這種意圖的實現。解決方法是,禁止創建臨時變量。因此這個調用也是錯誤的。如果函數調用的參數是右值或與相應的const引用參數的類型不匹配,則C++將創建類型正確的匿名變量,將函數調用的參數的值傳遞給該匿名變量,並讓參數來引用該變量