在 c++11 的新標準中,出現了「右值引用」的說法,既然有了右值引用,那麼傳統的引用也就叫做左值引用。
右值引用 (rvalue referene) 是 c++ 新標準 (c++11, 11 代表 2011 年 ) 中引入的新特性 , 它實現了轉移語義 (move sementics) 和精確傳遞 (perfect forwarding)。它的主要目的有兩個方面:
消除兩個物件互動時不必要的物件拷貝,節省運算儲存資源,提高效率。
能夠更簡潔明確地定義泛型函式。
c++( 包括 c) 中所有的表示式和變數要麼是左值,要麼是右值。通俗的左值的定義就是非臨時物件,那些可以在多條語句中使用的物件。所有的變數都滿足這個定義,在多條**中都可以使用,都是左值。右值是指臨時的物件,它們只在當前的語句中有效。請看下列示例 :
簡單的賦值語句
如:int i = 0;
在這條語句中,i 是左值,0 是臨時值,就是右值。在下面的**中,i 可以被引用,0 就不可以了。立即數都是右值。
右值也可以出現在賦值表示式的左邊,但是不能作為賦值的物件,因為右值只在當前語句有效,賦值沒有意義。
如:((i>0) ? i : j) = 1;
在這個例子中,0 作為右值出現在了」=」的左邊。但是賦值物件是 i 或者 j,都是左值。
在 c++11 之前,右值是不能被引用的,最大限度就是用常量引用繫結乙個右值,如 :
const int &a = 1;
在這種情況下,右值不能被修改的。但是實際上右值是可以被修改的,如 :
t().set().get();
t 是乙個類,set 是乙個函式為 t 中的乙個變數賦值,get 用來取出這個變數的值。在這句中,t() 生成乙個臨時物件,就是右值,set() 修改了變數的值,也就修改了這個右值。
既然右值可以被修改,那麼就可以實現右值引用。右值引用能夠方便地解決實際工程中的問題,實現非常有吸引力的解決方案。
本小節,談「引用」與「左值引用」同義。
引用必須在定義時進行初始化被引用變數名可以是結構變數成員,如s.m
函式引數可以是引用型別,表示函式的形式引數與實際引數是同乙個變數,改變形參將改變實參
函式返回值可以是引用型別,但不得是函式的臨時變數。
引用的定義格式:右值引用是 c++11 新標準裡面新增加的一種引用型別。引用的性質:資料型別& 變數名稱 = 被引用變數名稱;
int a;
int& ref = a;
引用的重要用途:
常量引用:既能引用常量,不能通過引用改變目標物件值;引用本身也不能改變引用物件
右值引用:
匿名變數(臨時變數)的別名:
型別名 && 引用名 表示式;
例如:
int
&& sum =3+
4;//初值是3 + 4 這個表示式
float
&& res =
returnrvalue
(f1,f2)
;
右值引用的典型應用是在函式引數中,例如:
void
acceptrvalueref
(t&& s)
目的是:減少函式拷貝開銷
左值的宣告符號為&
, 為了和左值區分,右值的宣告符號為&&
。
#include
using
namespace std;
void
process_value
(int
&i)void
process_value
(int
&&i)
intmain()
執行結果:
lvalue processed: 0
rvalue processed: 1
process_value 函式被過載,分別接受左值和右值。由輸出結果可以看出,臨時物件是作為右值處理的。
但是如果臨時物件通過乙個接受右值的函式傳遞給另乙個函式時,就會變成左值,因為這個臨時物件在傳遞過程中,變成了命名物件。比如:
#include
using
namespace std;
void
process_value
(int
&i)void
process_value
(int
&&i)
void
forward_value
(int
&&i)
intmain()
結果:
lvalue processed: 0
rvalue processed: 1
lvalue processed: 2
雖然 2 這個立即數在函式 forward_value 接收時是右值,但到了 process_value 接收時,變成了左值。
前面說了那麼多,那麼右值引用的意義到底體現在**呢,明明我不用引用也可以實現,為什麼要用右值引用呢,因為在對效能要求較高時,我們必須減少不必要的記憶體消耗,節省儲存資源,比如以下例子:
#include
using
namespace std;
int g_constructcount =0;
int g_copyconstructcount =0;
int g_destructcount =0;
struct aa(
const a &a)~a
()};
a geta()
intmain()
為了觀察清楚這個程式真正的變化,再編譯的時候加上-fno-elide-constructors
關閉返回值優化效果。
輸出結果:
construct: 1
copy construct: 1
destruct: 1
copy construct: 2
destruct: 2
destruct: 3
很清楚的可以看到,在沒有返回值優化的情況下,拷貝構造函式呼叫了兩次,一次是geta()函式內部建立的物件返回出來構造乙個臨時物件產生的,另一次是在main函式中構造a物件產生的。第二次的destruct是因為臨時物件在構造a物件之後就銷毀了。
那麼我們把這個程式稍微改改,如果用右值引用來繫結函式返回值的話,結果會是什麼樣子呢?首先修改**
int
main()
輸出結果:
construct: 1
copy construct: 1
destruct: 1
destruct: 2
通過右值引用,比之前少了一次拷貝構造和一次析構,原因在於右值引用繫結了右值,讓臨時右值的生命週期延長了。
所以,通過右值引用,可以讓乙個臨時物件重獲新生,在它完成任務時不用被銷毀,而是生存期與引用他的那個變數一樣長
以及乙個知乎問題:
C 11 左值引用與右值引用
左值,右值 英文並不是left value和right value,而是指記憶體儲存訪問的方式,左值有固定記憶體訪問位址 例如有名字的變數 右值沒有固定的記憶體訪問位址 例如函式的返回值 右值應用的作用 避免老的標準 現無謂的效能消耗,把無記憶體訪問的生命週期延長。在 c 11 的新標準中,出現了 ...
C 11 左值 右值 右值引用詳解
在c 11中所有的值必屬於左值 右值兩者之一,右值又可以細分為純右值 將亡值。在c 11中可以取位址的 有名字的就是左值,反之,不能取位址的 沒有名字的就是右值 將亡值或純右值 舉個例子,int a b c,a 就是左值,其有變數名為a,通過 a可以獲取該變數的位址 表示式b c 函式int fun...
C 11 左值 右值 右值引用詳解
在c 11中所有的值必屬於左值 右值兩者之一,右值又可以細分為純右值 將亡值。在c 11中可以取位址的 有名字的就是左值,反之,不能取位址的 沒有名字的就是右值 將亡值或純右值 舉個例子,int a b c,a 就是左值,其有變數名為a,通過 a可以獲取該變數的位址 表示式b c 函式int fun...