右值引用和物件移動

2021-08-25 22:37:34 字數 3825 閱讀 3064

c++( 包括 c) 中所有的表示式和變數要麼是左值,要麼是右值。通俗的左值的定義就是非臨時物件,那些可以在多條語句中使用的物件。 所有的變數都滿足這個定義,在多條**中都可以使用,都是左值。右值是指臨時的物件,它們只在當前的語句中有效。

為了支援移動操作,c++11引入了一種新的引用型別-右值引用。就是必須繫結到右值的引用。我們通過&&而不是&來獲得右值引用。右值引用只能繫結到乙個將要銷毀的物件。

由於右值引用只能繫結到臨時物件,我們得知:

所引用的物件將要被銷毀

該物件沒有其他使用者

這兩個特性意味著:使用右值引用的**可以自身自由的接管所引用的物件的資源。

變數可以看作只有乙個運算物件而沒有運算子的表示式。

int i = 23;

int &&j = i;//錯誤:不能將乙個右值引用繫結到乙個左值上

int &&k = i * 3;//正確,i*3是乙個右值

move函式理解之前先要知道左值、左值引用、右值、右值引用的概念。

雖然不能將乙個右值引用直接繫結到乙個左值上,但可以通過呼叫乙個名為move的新標準庫函式來獲得繫結到左值上的右值引用。

int i=6;

int &&l=std::move(i);

對比下面操作第二段**使用了move語句,而第一段**沒有使用move語句。輸出的結果差異也很明顯,第二段**中,原來的字串st已經為空,而第一段**中,原來的字串st的內容沒有變化。

int main()

int main()

在很多情況下都會發生物件拷貝,比如利用reallocate重新分配記憶體的過程,在其中某些情況下,物件拷貝後就立即被銷毀了。在這些情況下,移動而非拷貝物件會大幅度提公升效能。

移動建構函式,引數是右值引用

strvec::strvec(strvec &&s) noexcept                 //noexcept指名不丟擲異常

:elements(s.elements), first_free(s.first_free),cap(s.cap)

與拷貝建構函式不同,移動建構函式不分配任何新記憶體:它接管給定的strvec中的記憶體。在接管記憶體後,它將給定物件中的指標都置為nullptr。

與處理拷貝建構函式和拷貝賦值運算子一樣,編譯器也會合成移動建構函式和移動賦值運算子。但是,合成移動構造的條件與合成拷貝操作的條件大不相同。

只有當乙個類沒有定義任何自己版本的拷貝控制成員(拷貝建構函式、拷貝賦值運算子、析構函式),且它的所有資料成員都能移動構造或移動賦值時,編譯器才會為它合成移動建構函式或移動賦值運算子。

如果乙個類定義了乙個移動建構函式或乙個移動賦值運算子,則該類的合成拷貝建構函式和拷貝賦值運算子被定義為刪除的。

除了建構函式和賦值運算子之外,成員函式也可以同時提供拷貝版本和移動版本。例如:

void push_back(const x&);    //拷貝:繫結到任意型別的x

void push_back(x&&); //移動:只能繫結到型別x的可修改的右值

關於此部分講解很好的博文

1.引用摺疊規則:

x& + & => x&

x&& + & => x&

x& + && => x&

x&& + && => x&&

2.函式模板引數推導規則(右值引用引數部分):當函式模板的模板引數為t而函式形參為t&&(右值引用)時適用本規則。

若實參為左值 u& ,則模板引數 t 應推導為引用型別 u& 。

(根據引用摺疊規則, u& + && => u&, 而t&& ≡ u&,故t ≡ u& )

若實參為右值 u&& ,則模板引數 t 應推導為非引用型別 u 。

(根據引用摺疊規則, u或u&& + && => u&&, 而t&& ≡ u&&,故t ≡ u或u&&,這裡強制規定t ≡ u )

3.std::remove_reference為c++0x標準庫中的元函式,其功能為去除型別中的引用。

std::remove_reference::type ≡ u

std::remove_reference::type ≡ u

std::remove_reference::type ≡ u

4.以下語法形式將把表示式 t 轉換為t型別的右值(準確的說是無名右值引用,是右值的一種)

static_cast(t)

5.無名的右值引用是右值

具名的右值引用是左值。

6.注:本文中 ≡ 含義為「即,等價於「。

函式功能:

std::move(t) 負責將表示式 t 轉換為右值,使用這一轉換意味著你不再關心 t 的內容,它可以通過被移動(竊取)來解決移動語意問題。

原始碼與測試**

templateinline typename std::remove_reference<_tp>::type&&

move(_tp&& __t)

#includeusing namespace std;

struct x {};

int main()

**說明:

測試**第9行用x型別的左值 a 來測試move函式,根據標準x型別的右值引用 b 只能繫結x型別的右值,所以 move(a) 的返回值必然是x型別的右值。

測試**第10行用x型別的右值 x() 來測試move函式,根據標準x型別的右值引用 c 只能繫結x型別的右值,所以 move(x()) 的返回值必然是x型別的右值。

首先我們來分析 move(a) 這種用左值引數來呼叫move函式的情況。

模擬單步呼叫來到原始碼第3行,_tp&& ≡ x&, __t  ≡ a 。

根據函式模板引數推導規則,_tp&& ≡ x& 可推出 _tp ≡ x& 。

typename std::remove_reference<_tp>::type ≡ x 。

typename std::remove_reference<_tp>::type&& ≡ x&& 。

再次單步呼叫進入move函式實體所在的原始碼第4行。

static_cast::type&&>(__t) ≡ static_cast(a)

根據標準 static_cast(a) 將把左值 a 轉換為x型別的無名右值引用。

然後我們再來分析 move(x()) 這種用右值引數來呼叫move函式的情況。

模擬單步呼叫來到原始碼第3行,_tp&& ≡ x&&, __t  ≡ x() 。

根據函式模板引數推導規則,_tp&& ≡ x&& 可推出 _tp ≡ x 。

typename std::remove_reference<_tp>::type ≡ x 。

typename std::remove_reference<_tp>::type&& ≡ x&& 。

再次單步呼叫進入move函式實體所在的原始碼第4行。

static_cast::type&&>(__t) ≡ static_cast(x())

根據標準 static_cast(x()) 將把右值 x() 轉換為x型別的無名右值引用。

由9和16可知原始碼中std::move函式的具體實現符合標準,

因為無論用左值a還是右值x()做引數來呼叫std::move函式,

該實現都將返回無名的右值引用(右值的一種),符合標準中該函式的定義。

C 物件移動(2) 右值引用

右值引用就是就是必須繫結 bind 到右值的引用。在c 11 誕生之前,廣泛使用的c 98 中的引用其實是左值引用,請看下面這個例子 int a 0 a is a lvalue int b a b is a lvalue reference before c 11,it s reference 何為...

右值引用和移動語義

在c 當中,所有的變數和表示式都被分為左值和右值。所謂的左值,就是可以被定址的非臨時變數。右值就是無法被定址的臨時變數。左值引用用 符號表示,右值引用用 符號表示。左值引用 int n int np new int const int cn 100 int nr n int npr np const...

C 移動語義和右值引用

移動語義類似於在計算機中移動檔案的情形 實際檔案還留在原來的地方,而只修改記錄。例如有乙個函式,它返回乙個vector物件 要實現移動語義,需要讓編譯器知道什麼時候需要複製,什麼時候不需要。這就是右值引用發揮作用的地方。可定義兩個建構函式,其中乙個是常規複製建構函式,它使用const左值引用作為引數...