1. 程式人生 > >C++引用—臨時變數、引用引數和const引用和左值右值引用

C++引用—臨時變數、引用引數和const引用和左值右值引用

如果實參與引用引數不匹配,C++將生成臨時變數。如果引用引數是const,則編譯器在下面兩種情況下生成臨時變數:

         實參型別是正確的,但不是左值

         實參型別不正確,但可以轉換為正確的型別

Double refcube(const double& ra)

{

        Returnra*ra*ra;

}

double side = 3.0;

double* pd = &side;

double& rd = side;

 long edge = 5L;

double lens[4]={2.3,3.4,4.5,6.7};

double c1 = refcube(side);  // ra 是side

double c2 = refcube(lens[2]); // ra是lens[2]

double c3 = refcube(rd);   // ra 是 rd

double c4 = refcube(*pd);  // ra 是*pd

double c5 = refcube(edge);  // ra 是臨時變數

double c6 = refcube(7.0);  // ra 是臨時變數

double c7 = refcube(side+10.0); // ra 是臨時變數引數side lens[2] rd 和*pd都是有名稱的、double型別的資料物件,因此可以為其建立引用,而不需要臨時變數。但是edge雖然是變數,型別卻不正確,double引用不能指向long。另一方面,引數7.0和side+10.0的型別都正確,但沒有名稱,在這些情況下,編譯器都將生成一個臨時匿名變數,並讓ra指向它。這些臨時變數只在函式呼叫期間存在

如果宣告將引用指定為const,C++將在必要時生成臨時變數、實際上,對於形參為const引用的C++函式,如果實參不匹配,則其行為類似於按值傳遞,為確保原始資料不被修改,將使用臨時變數來儲存值

       應儘可能使用const

使用cosnt可以避免無意總修改資料的程式設計錯誤

使用const使函式能夠處理const和非const實參,否則將只能接受非const資料

使用const引用使函式能夠正確生成並使用臨時變數

const修飾左值引用

int & r = val + 1; //此句不合法,因為右值無法賦值給左值引用
const int& r = val + 1;

//合法

右值引用

在上面的程式碼中,我們無法建立 int &rb = a + 1; 這樣的語法,因為a + 1 此時是作為一個右值來使用的,我們無法把一個右值賦值給一個左值引用。(也就是左值引用相當於把一個變數的地址付給另一個變數,這兩個變數可以訪問同一個記憶體,右值僅僅是一個數,而非記憶體中的某塊地址,因此無法把右值複製給左值引用)。

宣告方法:Type && 右值引用名 = 右值表示式;

std::move()的用法

可以直接把左值或者右值轉換成右值引用,使用方法:

int && rrval = std::move(val);

但是這裡需要注意:在呼叫完std::move之後,不能再使用val,只能使用 rrval,這一點用於基本型別可能沒什麼直接影響,當應用到類函式的時候,用好std::move 可以減少建構函式數的次數

右值引用

為了解決移動語義及完美轉發問題,C++11標準引入了右值引用(rvalue reference)這一重要的新概念。右值引用採用T&&這一語法形式,比傳統的引用T&(如今被稱作左值引用 lvalue reference)多一個&。
如果把經由T&&這一語法形式所產生的引用型別都叫做右值引用,那麼這種廣義的右值引用又可分為以下三種類型:

  • 無名右值引用
  • 具名右值引用
  • 轉發型引用

無名右值引用和具名右值引用的引入主要是為了解決移動語義問題。
轉發型引用的引入主要是為了解決完美轉發問題。

 無名右值引用

無名右值引用(unnamed rvalue reference)是指由右值引用相關操作所產生的引用型別。
無名右值引用主要通過返回右值引用的型別轉換操作產生, 其語法形式如下:
static_cast<T&&>(t)
標準規定該語法形式將把表示式 t 轉換為T型別的無名右值引用。
無名右值引用是右值,標準規定無名右值引用和傳統的右值一樣具有潛在的可移動性,即它所佔有的資源可以被移動(竊取)。

 std::move()

由於無名右值引用是右值,藉助於型別轉換操作產生無名右值引用這一手段,左值表示式就可以被轉換成右值表示式。為了便於利用這一重要的轉換操作,標準庫為我們提供了封裝這一操作的函式,這就是std::move()。
假設左值表示式 t 的型別為T&,利用以下函式呼叫就可以把左值表示式 t 轉換為T型別的無名右值引用(右值,型別為T&&)。
std::move(t)

 具名右值引用

如果某個變數或引數被宣告為T&&型別,並且T無需推導即可確定,那麼這個變數或引數就是一個具名右值引用(named rvalue reference)。
具名右值引用是左值,因為具名右值引用有名字,和傳統的左值引用一樣可以用操作符&取地址。
與廣義的右值引用相對應,狹義的右值引用僅限指具名右值引用。
傳統的左值引用可以繫結左值,在某些情況下也可繫結右值。與此不同的是,右值引用只能繫結右值。
右值引用和左值引用統稱為引用(reference),它們具有引用的共性,比如都必須在初始化時繫結值,都是左值等等。

struct X {};

X a;

X&& b = static_cast<X&&>(a);

X&& c = std::move(a);

//static_cast<X&&>(a) 和 std::move(a) 是無名右值引用,是右值

//b 和 c 是具名右值引用,是左值

X& d = a;

X& e = b;

const X& f = c;

const X& g = X();

X&& h = X();

//左值引用d和e只能繫結左值(包括傳統左值:變數a以及新型左值:右值引用b)

//const左值引用f和g可以繫結左值(右值引用c),也可以繫結右值(臨時物件X())
//右值引用b,c和h只能繫結右值(包括新型右值:無名右值引用std::move(a)以及傳統右值:臨時物件X()