非原創,由網上大神的答案拼湊而成。
左值與右值的定義:
簡言之:變量是左值,可以出現在賦值語句的左邊;數字字面值是右值,不能出現在賦值語句左邊。
詳細:左值可以出現在賦值語句的左邊或者右邊,即,左值也可以當右值用,右值只能出現在賦值的右邊,左值表示程序有特定的名字引用到這個值,右值表示程序中沒有特定的名字引用這個值。變量是左值,因此可以出現在賦值語句的左邊;數字字面值是右值,因此不能被賦值。實際上,左值是一個存儲地址,是一塊內存要存儲數據所要操作的地址;而右值是是一個具體的數據或者數值,也就是內存存儲的數據內容。
int a=1; //變量a是一個左值
char str[]="hello world"; //數組成員str[i]是一個左值"hello world"是一個右值
string("hello world"); 是一個右值
左值引用於右值引用:
左值引用:
用法:Type & 左值引用名=左值表達式;
例如: int &a=b;
右值引用:
a 爲一個變量,我們無法建立 int &rb=a+1; 這樣的語法,因爲 a+1 此時是作爲一個右值來使用的, 我們無法把一個右值賦值給一個
左值引用。(也就是左值引用相當於把一個變量的地址賦值給另外一個變量,這兩個變量可以訪問同一個內存,右值僅僅是一個數,而非內存中的某塊地址,因此無法把右值賦值給左值引用)。
可用過下面語法實現:
Type && 右值引用名=右值表達式;
int &&rb=a+1;
參考:https://www.cnblogs.com/cly-blog/p/5980546.html
引用摺疊:
Class A {
A()
{// do something}
};
A GetA()
{
return A();
}
int main()
{
A a1 = GetA(); // a1是左值
A&& a2 = GetA(); // a2是右值引用
return 0;
}
a1是左值,在構造時使用了GetA() 產生的臨時對象,之後GetA()產生的臨時對象會銷燬。
a2是右值引用,其指向的就是GetA()所產生的對象,這個對象的聲明週期是和a2的聲明週期是一致的。即少了臨時對象,從而省去了臨時對象的構造和析構。
由此可見右值引用的好處,在新代碼中,右值引用是值得大力使用的。但是,在使用的時候,有例外情況了:T&&並不是一定表示右值,比如,如果它綁定的類型是未知的話,既可能是左值,又可能是右值。比如:
template<typename T>
void f(T&& param);
f(10); // 10是右值
int x = 10;
f(x); // x是左值
以上這種未定的引用類型(param的類型)稱爲 universal references,這種類型必須被初始化,而它是左值還是右值則取決於它的初始化,如果被左值初始化,那麼它就是左值,反之亦然。那麼什麼時候是左值,什麼時候是右值,就需要進行類型推導才知道。
由於存在T&&這種未定的引用類型,當它作爲參數時,有可能被一個左值引用或右值引用的參數初始化,這是經過類型推導的T&&類型,相比右值引用(&&)會發生類型的變化,這種變化就稱爲引用摺疊。(《深入應用C++11-代碼優化與工程級應用》 --- 祁宇 P68
引用摺疊的規則如下(配合@jun-jun的答案)[和上一段的出處一樣]:
1.所有右值引用摺疊到右值引用上仍然是一個右值引用。(A&& && 變成 A&&)
2.所有的其他引用類型之間的摺疊都將變成左值引用。 (A& & 變成 A&; A& && 變成 A&; A&& & 變成 A&)
參考:https://www.zhihu.com/question/40346748