的引用_左值、右值、左值引用、右值引用
【導讀】:本文主要詳細介紹了左值、右值、左值引用、右值引用以及move、完美轉發。
左值和右值
左值(left-values),縮寫:lvalues
右值(right-values),縮寫:rvalues
直接上官網查,我一向倡導自己去懂得原理,而原理都是老外寫的,當然我只是針對c 編程語言這樣說。
https://msdn.microsoft.com/en-us/library/f90831hc.aspx
翻譯:所有的c 表達,不是左值就是右值。
lvalues是指存在于單個表達式之外的對象。你可以把左值當成有名字的對象。所有的變量,包括常變量,都是左值。
rvalues是一個暫時存在的值存在于單個表達式之內的對象。
有點拗口(難理解),通俗來說就是,左值的生存期不只是這句話,后面還能用到它。
而右值呢,出了這句話就掛了,所以也叫(將亡值)。
它舉了一個栗子:
#include???using?namespace?std;??
int?main()??{??
???int?x?=?3? ?4;??
???cout?<endl;??
}??
在以上實例中,很顯然,x是左值,3 4是右值。
它又舉了一個栗子,來說明錯誤的使用和正確的使用
//?lvalues_and_rvalues2.cpp??int?main()??{??
???int?i,?j,?*p;??
??
???//?正確的使用:?變量是左值?
???i?=?7;??
??
???//?錯誤的使用:?左邊的操作?必須是?左值?(C2106)
???7?=?i;?//?C2106??
???j?*?4?=?7;?//?C2106??
??
???//?正確的使用:?被間接引用的指針是左值
???*p?=?i;???
??
???const?int?ci?=?7;??
???//?錯誤的使用:?左邊的操作?是?常量左值?(C3892)
???ci?=?9;?//?C3892?
??
???//?正確的使用:?條件操作?返回了左值
???((i?3)???i?:?j)?=?7;??
}??
左值引用、右值引用
左值引用:參考說明書《Lvalue Reference Declarator: &》,網站如下:
https://msdn.microsoft.com/en-us/library/w7049scy.aspx
使用語法:類型 &(引用符) 表達式
type-id?&?cast-expression?翻譯:
你可以把左值引用當成對象的另一個名字,lvalue引用聲明由一個可選的說明符列表和一個引用聲明符組成。
引用必須初始化,而且不能改變。
一個對象的地址可以 轉化成 一種指定類型的指針 或者 轉化成 一個 相似類型的引用。意義是相同的。
demo:
char?c_val?=?'c';char?*ptr?=?&c_val;
char?&r_val?=?c_val;
不要混淆 取地址 和 引用,當&說明符前面帶有類型聲明,則是引用,否則就是取地址。
通俗來說 &在 ”=” 號左邊的是引用,右邊的是取地址。
右值引用:參考說明書《Rvalue Reference Declarator: &&》,網站如下:
https://msdn.microsoft.com/en-us/library/dd293668.aspx
使用語法:類型 && 表達式
type-id?&&?cast-expression??翻譯:
Move Semantics:移動語義
右值引用使您能夠區分左值和右值。Lvalue引用和rvalue引用在語法和語義上是相似的。
右值引用支持移動語義的實現,可以顯著提升應用程序的性能。移動語義允許您編寫將資源(例如動態分配的內存)從一個對象傳輸到另一個對象的代碼,移動語義行之有效,因為它允許從程序中其他地方無法引用的臨時對象轉移資源。
為了實現移動語義,你在類中提供一個動態構造,和可選擇的動態賦值運算符(operator=)。拷貝和賦值操作的資源是右值的可以自動調用移動語義。不像缺省的拷貝構造,編譯器并不提供缺省的動態構造。
demo:
#include???#include???
using?namespace?std;??
??
int?main()??{??
???string?s?=?string("h")? ?"e"? ?"ll"? ?"o";??
???cout?<endl;??
}??
在Visual C 2010之前,每個調用 “ ”運算符會分配和返回一個新的臨時的string對象,
“ ”運算符不能從一個string擴展到另一個,因為它不知道string是左值還是右值。如果源字符串都是lvalues,那么它們可能在程序的其他地方被引用,因此不能被修改。通過使用右值引用“ ”運算符能夠修改那些不能在程序中別處引用的右值,所以現在“ ”運算符可以有一個string擴展到另一個。這可以顯著減少字符串類必須執行的動態內存分配的數量。
為了更好地理解移動語義,考慮向向量對象插入一個元素的例子。如果超出了vector對象的容量,vector對象必須為其元素重新分配內存,然后將每個元素復制到另一個內存位置,以便為插入的元素騰出空間。當插入操作復制一個元素時,它創建一個新元素,調用copy構造函數將數據從前一個元素復制到新元素,然后銷毀前一個元素。移動語義允許您直接移動對象,而不必執行昂貴的內存分配和復制操作。
Perfect Forwarding:完美轉發
完美的轉發減少了重載函數 避免了轉發的問題。轉發的問題出現在你寫通用函數將引用作為參數,將這些參數由函數調用的時候。
舉個例子,如果通用函數將 type const T&作為參數,那么調用函數不能修改參數的值。
如果通用函數 將 type T&作為參數,那么當參數是右值的時候,函數不能調用。
通常來說,為了解決上述的問題,你需要提供重載函數,既要有type const T&參數的函數,也要有type T&參數的函數。
結果呢,重載函數的數量隨著參數數量呈指數遞增。而右值引用能夠使你只用一個函數就能適用于任意數量的參數。
原先的做法如下:
先寫出所有適用的通用函數
struct?W??{??
???W(int&,?int&)?{}??
};??
??
struct?X??
{??
???X(const?int&,?int&)?{}??
};??
??
struct?Y??
{??
???Y(int&,?const?int&)?{}??
};??
??
struct?Z??
{??
???Z(const?int&,?const?int&)?{}??
};??
再將帶有不同類型的參數的函數用模板結合起來
template?<typename?T,?typename?A1,?typename?A2>??T*?factory(A1&?a1,?A2&?a2)??{??
???return?new?T(a1,?a2);??
}??
調用:需要根據適用的類型用相應的指針對接。
當調用的是左值時
int?a?=?4,?b?=?5;??W*?pw?=?factory(a,?b);??
當調用的是右值時。但是,下面的示例中沒有包含對工廠函數的有效調用,因為工廠將可修改的lvalue引用作為其參數,但是它是通過使用右值調用的:
這里要注意的是const int &是lvalue 而不是 rvalue
而2是rvalue,函數會編譯不過。
Z*?pz?=?factory(2,?2);??為了解決這類問題,需要將模板函數修改成如下形式,右值引用可以適用const T& 和 T&形式的參數:
template?<typename?T,?typename?A1,?typename?A2>??T*?factory(A1&&?a1,?A2&&?a2)??{??
???return?new?T(std::forward(a1),?std::forward(a2));??
}??
經過上述修改,均可以調用,如下圖代碼所示:
總結
以上是生活随笔為你收集整理的的引用_左值、右值、左值引用、右值引用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 虚拟机镜像xp\win7\win10\w
- 下一篇: 我感觉我恰似一个呆逼