lvalue 引用 && rvalue 引用


C++中的每個表達式要么是lvalue要么是rvalue。lvalue表示一個內存位置,而rvalue表示計算表達式的結果。

rvalue引用是對有名稱變量的引用,並允許變量表示的內存通過lvalue引用來訪問。

rvalue引用是對包含表達式結果的內存位置的引用。

總之,表達式的結果和函數內定義的變量都屬於臨時變量,即rvalue。

  int && num {2*x+3};   //rvalue引用表達式的臨時結果

  int & num {x=5};    //lvalue引用

常量引用:

void GetSet(int &num)
{
 num += 1;
}

 Error (active)  initial value of reference to non-const must be an lvalue.

void GetSet(const int &num)
{
 num += 1;
}

上面代碼:只看第一個函數頭GetSet(變量)是正確的,但GetSet(常量5)會被報錯,因為編譯器不允許對常量存在可能潛在的更改行為。

     規定常量作為參數傳遞時需加const,即意味着不可更改。

lvalue引用:

使用lvalue引用形參,可以編寫直接訪問調用者實參的函數,避免了按值傳遞中的隱式復制。若不打算修改實參,則只需要給lvalue引用類型使用const修飾符,以避免意外修改參數。

其實,無論是按值傳遞、按址傳遞參數或引用都是編譯器的規則,我們需要熟悉參數在不同情況下的傳遞,好的理解方式就是輸出地址來觀察。

void GetSet(int & num)
{
     num += 1;
}
int main()
{
     int v = 6;
     GetSet(v);
     cout << v<< endl;
     cin.get();
}

輸出結果: 7 ,num變量值通過引用改變了,可以類比,變量的指針傳址方式。

rvalue引用:

void GetSet(int && num)
{
     num += 1;
}
int main()
{
     int v = 6;
     GetSet(v);
     cout << v<< endl;
     cin.get();
}

編譯器報錯:Error (active)  an rvalue reference cannot be bound to an lvalue 

可知lvalue不能通過rvalue引用,有rvalue引用形參的函數只能通過rvalue實參來調用。

void GetSet(int && num)
{
    num += 1;
    cout <<"num="<< num << endl;
}
int main()
{
    int v = 6;
    int c = 3;
    GetSet(v+c);
    cout <<"v="<< v <<" c="<< c <<endl;
    GetSet(5);
    cin.get();
}

上面代碼編譯通過,結果如下:

編譯器會為表達式的結果生成一個臨時地址來存儲數值。而常量字面值 5 ,被當成一個表達式處理,並且存儲在函數引用形參的臨時位置。

輸出地址可以看出,常量在rvalue引用的時候和表達式一樣處理了,即看做臨時變量。

函數返回值的引用:

int* GetSet(int num)
{
    num += 1;
    int result {num};
    return & result;
}
int main()
{
    int v = 6;
    int *ptr=GetSet(v);
    cout <<"ptr="<< *ptr << endl;
}

本段代碼可以運行,但編譯器卻給出了如下警告:
“Warning C4172 returning address of local variable or temporary”

原因在於:從函數中返回的引用是臨時變量的地址,應注意不要從函數中返回局部自動變量的地址。

可以使用動態內存分配為函數中的變量申請內存,由於動態內存分配的內存是一直存在的(在堆中),除非用 delete 銷毀。即如下方式:

int* GetSet(int num)
{
    num += 1;
    int *result{ new int {num} };
    return result;
}
int main()
{
    int v = 6;
    int *ptr=GetSet(v);
    cout <<"ptr="<< *ptr << endl;
   delete ptr; }

 觀察下面兩部分代碼的不同:

一、
double
& lowest(double a[], int len) { int j{}; for (int i{ 1 }; i<len; i++) if (a[j]>a[i]) j = i; return a[j]; }
二、
double
& lowest(double a[], int len) { int j{}; for (int i{ 1 }; i<len; i++) if (a[j]>a[i]) j = i; return & a[j]; }

引用是賦予存在變量的別名,所以實際返回的是數組元素a[j]的引用,而不是改元素包含的值。a[j]的地址用來初始化要返回的引用,該引用是編譯器創建的,因為返回類型是引用。

但返回a[j]和返回&a[j]是不同的。

返回&a[j],則指定的是a[j]地址,那是一個指針。所以,第二部分代碼會被報錯如下:
  Error C2440 'return': cannot convert from 'double *' to 'double &' 

因為指針和引用屬於不同類型。

Const 在傳參引用中的潛規則的:

double refcube(const double &ra)
{
    return ra*ra*ra;
}

    long edge = 5L;
    double side = 3.0;
    double c1 = refcube(edge);
    double c2 = refcube(7.0);
    double c3= refcube(side+10.0);

上面代碼中的函數調用都會產生臨時變量:

  edge雖然是臨時變量,類型不正確,double引用不能指向long。

  參數7.0和side+10.0類型雖然正確,但沒有名稱,編譯器將生成一個函數期間調用的匿名臨時變量。

而現在如果我們去掉 const ,會發現編譯器會報錯:

Error:a reference of type "double &" (not const-qualified) cannot be initialized with a value of type "long"

Error:initial value of reference to non-const must be an lvalue 

Why?

double refcube(double &ra) 是lvalue引用,要求使用滿足類型的左值作為參數。

那么為何 double refcube(double ra) 會通過編譯。

  因為直接調用和使用const一樣編譯器會產生 臨時變量,只不過使用了 const 意味着不能修改參數。

  1. 使用const可以避免無意中修改數據。
  2. 能夠處理const和非const實參,否則只能接受非const數據。
  3. 使用const引用使函數能夠正確生成和使用臨時變量。

當然此處代碼是可以使用rvalue引用或const rvalue引用,但 Long 類型沒通過報錯如下:

Error:'double refcube(const double &&)': cannot convert argument 1 from 'long' to 'const double &&'     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM