2014-10-07 45 views
0

在閱讀有關move constructor從目前standard,我可以看到以下這一點:瞭解默認移動構造函數定義

12.8複製和移動類對象

如果一個類的定義X沒有明確聲明一個移動構造函數,只有當且僅當

- X沒有用戶聲明的複製構造函數,
- X沒有用戶聲明的複製賦值運算符,
- X沒有用戶聲明的移動賦值運算符,並且
- X沒有用戶聲明的析構函數。

[注意:當移動構造函數未被隱式聲明或顯式提供時,否則將調用移動構造函數的表達式可能會調用複製構造函數。 - 結束注意]

我覺得注意部分清楚地提到,默認移動構造函數的後退將是複製構造函數。爲了理解這個概念,我編寫了一個小程序來理解這個概念。

#include<iostream> 
struct handleclass { 
public: 
    handleclass():length{0}, p{nullptr} {} 
    handleclass(size_t l):length{l},p{new int[length]} { } 
    ~handleclass() { delete[] p; } 
    size_t length; 
    int* p; 
}; 

handleclass function(void) { 
    handleclass x(10); 
    return x; 
} 

int main() { 
    handleclass y; 
    std::cout<<y.length<<std::endl; 
    y = function(); 
    std::cout<<y.length<<std::endl; 

    handleclass a; 
    handleclass b(10); 
    a = std::move(b); 

    return 0; 
} 

顯然這個方案是不正確,將由兩個對象有不確定的操作(終止)由於資源的淺拷貝。但我的重點是瞭解程序中生成和使用的默認移動構造函數。我希望這個例子有意義。

在上面的程序中,在移動構造函數應該被調用的兩種情況下,在我看來,編譯器正在使用默認的拷貝構造函數。

根據標準中提到的上述規則,我認爲我們應該得到編譯器錯誤,因爲現在程序顯式地嘗試調用移動構造函數,並且用戶既沒有執行也沒有編譯器生成默認(隱式),如上面的規則不滿足?。

但是,這是得到編譯沒有任何警告/錯誤,併成功運行。有人可以解釋有關默認(隱式)移動構造函數的概念嗎?或者我錯過了什麼?

回答

1

您忘記了copy elision這意味着y = function();可能實際上不會調用任何副本或移動構造函數;只是x的構造函數和賦值運算符。

有些編譯器可以讓你禁用copy elision,正如該線程中提到的那樣。

我不確定你的意思是「在應該調用移動構造函數的兩種情況下」。實際上,在調用移動構造函數(您的對象沒有移動構造函數)的情況下,以及可以調用複製構造函數的一種情況(return語句)可能不存在,但可能會被忽略。

你有兩種情況賦值運算符y = function();a = std::move(b);。同樣,因爲你的班級沒有移動賦值操作符,所以這些操作符將使用拷貝賦值操作符。

如果您從複製構造函數和移動構造函數中將代碼添加到cout的對象,它可能會有助於您的測試。

+0

-fno-elide-constructors選項可用於禁用g ++的複製elision – 2014-10-07 02:01:16

1

事實上,由於用戶聲明的析構函數,沒有隱式移動構造函數。

但有一個隱式拷貝構造函數和複製賦值操作符;由於歷史原因,析構函數不會抑制這些,儘管這種行爲已被廢棄,因爲(正如您指出的那樣)它通常會給出無效的複製語義。

它們可以用來複制兩個左值右值,因此被用於函數返回(可能被省略)和任務在您的測試程序。如果你想防止這種情況,那麼你將不得不刪除這些功能。

1

移動構造函數和移動賦值運算符未被隱式聲明,因爲您已明確定義了析構函數。儘管已經隱式聲明瞭拷貝構造函數和拷貝賦值操作符(儘管此行爲已被廢棄)。

如果移動構造函數和移動賦值操作符未被隱式(或顯式)聲明,它將回退到使用拷貝等效項。

當您嘗試調用移動分配時,它將回退到使用複製分配。