1. 程式人生 > >C++中讓人忽視的左值和右值

C++中讓人忽視的左值和右值

前言

為了瞭解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++將建立型別正確的匿名變數,將函式呼叫的引數的值傳遞給該匿名變數,並讓引數來引用該變數

 

轉載自:https://www.cnblogs.com/SimonKly/p/7873589.html