標籤:編譯 語言 函數返回 傳回值 生命週期 指標 pre div 函數參數
前言
為了瞭解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++將建立類型正確的匿名變數,將函數調用的參數的值傳遞給該匿名變數,並讓參數來引用該變數
C++中讓人忽視的左值和右值