C++ 左值與右值

最近一段時間寫C++代碼,總是被其中的左值/右值以及std::move等東西困擾,打算用文章記錄下我學(xué)習(xí)這部分知識的過程,做個總結(jié),給自己以后做參考;這邊文章首先介紹下C++中的左值/右值以及左值引用和右值引用。

左值與右值

直觀來說,在一個表達(dá)式中,出現(xiàn)在等號左邊的表達(dá)式是左值,出現(xiàn)在等號右邊的表達(dá)式是右值;注意,這里強調(diào)是表達(dá)式而不是變量。左值既可以出現(xiàn)在等號的左邊也可以出現(xiàn)在等號的右邊,而右值一定只能出現(xiàn)在等號的右邊。一個表達(dá)式如果不是左值,一定是右值。

本質(zhì)上說,左值是可以被尋址的值,即左值一定對應(yīng)內(nèi)存中的一塊區(qū)域,而右值既可以是內(nèi)存中的值,也可以是寄存器中的一個臨時變量。一個表達(dá)式被用作左值時,使用的是它的地址,而被用作右值時,使用的是它的內(nèi)容。

如何準(zhǔn)確的判斷一個表達(dá)式是不是左值呢?

  • 如果這個表達(dá)式可以出現(xiàn)在等號的左邊,一定是左值;
  • 如果可以對一個表達(dá)式使用&符號去地址,它一定是左值;
  • 如果它“有名字”,則一定是左值;
int a = b+c ;  // a是左值,因為我們可以對a取地址 &a; b+c是右值,因為我們不能對它取地址 &(b+c)編譯會報錯

在cpp11中,右值又可以進(jìn)一步分為純右值(pure rvalue)和將亡值(expiring value);其中純右值指的是臨時變量或者不和變量關(guān)聯(lián)的字面量;其中臨時變量包括非引用類型的函數(shù)返回值,比如 int f()的返回值,和表達(dá)式的結(jié)果,比如(a+b)結(jié)果就是一個臨時變量;字面量比如10,“abc”這些。將亡值是cpp11新增的,適合右值引用相關(guān)的概念,包括返回右值引用T&&的函數(shù)的返回值,std::move的返回值。

左值引用和右值引用

引用

引用在使用上是變量的一個別名,對引用變量的任何操作都可以理解為對原變量的操作;引用和指針的主要區(qū)別如下:

  • 引用不能為空,引用任何時候都要指向一塊合法的內(nèi)存;指針可以為空;
  • 引用一旦初始化完成,就不能再指向另一個變量;指針可以;
  • 引用必須在創(chuàng)建的時候初始化,指針可以在任何時候初始化;

那么什么是左值引用和右值引用呢?

左值引用就是對一個左值進(jìn)行引用的類型,右值引用就是對一個右值進(jìn)行引用的類型。左值引用和右值引用都屬于引用類型,都必須在聲明時對其進(jìn)行初始化,其原因是引用類型本身并不持有所引用變量的內(nèi)存,所以必須在初始化時制定要綁定的變量;左值引用是對具名變量的引用,右值引用是對不具名變量(匿名變量)的引用。

形如 const T&的常量左值引用是個萬金油的引用類型,其可以引用非常量左值,常量左值和右值。而形如T&的非常量左值引用只能接受非常量左值對其進(jìn)行初始化。


int &a = 2;       # 非常量左值引用綁定到右值,編譯失敗
int b = 2;        # 非常量左值變量
const int &c = b; # 常量左值引用綁定到非常量左值,編譯通過
const int d = 2;  # 常量左值
const int &e = c; # 常量左值引用綁定到常量左值,編譯通過
const int &b =2;  # 常量左值引用綁定到右值,編程通過

右值引用通常不能綁定任何左值,如果要綁定左值,需要使用std::move()將左值轉(zhuǎn)換為右值;

int a;
int&& r1 = a; //非法,a是左值;
int&& r2 = std::move(a); //合法,通過std::move()將左值轉(zhuǎn)換為右值;

下面來看一個例子:

void foo(int& a) {
    cout << "lvalue reference " << a << endl;
}

void foo(int&& a ) {
    cout << "rvalue reference " << a << endl;
}

int main() {
    int a = 0;
    foo(a);
    foo(1);
}

輸出:
lvalue reference 0
rvalue reference 1

上面的demo重載了foo函數(shù),使其既可以接受左值引用,也可以接受右值引用。從結(jié)果可以看出,變量是作為左值處理的,而字面常量(臨時對象)是作為右值處理的。

但是如果臨時對象通過一個接受右值的函數(shù)傳遞給另一個函數(shù),就會變成左值,因為這個變量在傳遞的過程中變成了具名變量:

void foo(int& a) {
    cout << "lvalue reference " << a << endl;
}

void foo(int&& a ) {
    cout << "rvalue reference " << a << endl;
}

void f(int&& a) {
    foo(a);
}

int main() {
    f(1);
}

輸出:
lvalue reference 1

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容