2013-12-08 66 views
8

擁有一個公共拷貝構造函數將會使小程序 編譯,但不會顯示副本「Copy」。爲什麼即使未被調用也需要公共拷貝構造函數?

#include <iostream> 

class X 
{ 
    public: 
    X(int) { std::cout << "Construct" << std::endl; } 

    // Having a public copy constructor will make the little program 
    // compile, but not showing the side effect "Copy". 

    private: 
    X(const X&) { std::cout << "Copy" << std::endl; } 

    private: 
    X& operator = (const X&); 
}; 

int main() { 
    X x = 1; 
    return 0; 
} 
+2

X x = 1意味着X x(X(1))就我所知,但它被優化到X x(1); – odinthenerd

+3

這是必需的,以便C++代碼在可能或不可以自行決定執行copy elision的實現之間是可移植的。 – jrok

+1

嘗試使用'-fno-elide-constructors'標誌進行編譯。 –

回答

4

下面是所涉及的C++標準的相關位:

[dcl.init]/16,子彈6,子子彈1:如果初始化直接初始化,或者如果它是複製初始化其中源類型的CV-不合格的版本是相同的類,或者派生類的,類目的地的,構造函數被認爲.... 如果沒有構造適用,或過載分辨率是模棱兩可,初始化不合格。 [強調]

換句話說,如果一個編譯器優化可能的Elid複製,因爲沒有適用的構造也沒關係,是形成不良的初始化。當然,一旦你讓副本constuctor公開,下面的部分適用:

[class.copy]/31:當滿足一定的條件,實現允許省略一類的複製/移動建設對象,即使該對象的複製/移動構造函數和/或析構函數具有副作用....在以下情況下,允許複製/移動操作的複製(稱爲複製省略)(可組合以消除多個副本):

bullet 3:當未綁定到引用(12.2)的臨時類對象將被複制/移動到具有相同cv-unqualified類型的類對象時,可以省略複製/移動操作通過構建臨時對象d直接進入被刪除的拷貝/移動的目標

2

我最好的猜測是這是一個編譯器優化。如果你有一個拷貝構造函數,就有一條以這種方式聲明一個對象的有效路徑。考慮這樣做明確:

int main() { 
    X x(X(1)); 
    return 0; 
} 

或者更明確:

int main() { 
    X intermediate(1); 
    X x(intermediate); 
    return 0; 
} 

因此,而不是使用operator=,它知道你想自申報來初始化對象。本質上,編譯器是「聰明的」。最後,再次優化了該到了這一步:

int main() { 
    X x(1); 
    return 0; 
} 

因此,編譯器計算出後,「你想幹什麼」這成爲對象的標準初始化。

編輯

爲了完整起見,請注意,如果你試試這個:

int main() { 
    X x = 1; 
    x = 2; 
    return 0; 
} 

您將看到的問題與私人operator=。明確地說,即使=出現在代碼中,這一點很重要,因爲operator=在上面的初始化初始化問題中從未實際使用過。

+0

哎呀應該沒有迴應,而不是評論;)恭喜。 – odinthenerd

+0

@PorkyBrain:如果'X x(X(1))'被優化爲'X x(1)',那麼您的評論會在這裏解決我的問題,謝謝 – RageD

+0

這裏的代碼完全不使用'operator ='。 'X x = 1'是'x'的直接初始化,並且不涉及賦值;它可能(但不必)調用複製構造函數。 –

2

您初始化是由編譯器優化到:

X x(1) 

這是一種複製省略的,並且是由標準允許的,即使它可以消除副作用,如你所看到的。

從C++ 03標準部分12.8:

當滿足特定條件時,一種實現被允許省略 一個類對象的拷貝結構,即使拷貝構造 和/或該對象的析構函數有副作用。在這種情況下, 實現將被忽略的副本 操作的源和目標視爲指向同一個對象的兩種不同方式,並且該對象的銷燬發生在兩個時間點的後期,其中兩個如果沒有進行優化,對象就會被銷燬。 111)在以下情況下(這可以合併以消除多個 副本)允許在 中複製操作的這種省略: - 在具有類返回類型的函數中的語句聲明, 當表達式是非重複類型的名稱時,通過將自動對象 直接構造到函數的返回值 中 - 當沒有綁定到的臨時類>對象時,可以省略 複製操作,該對象具有與函數返回類型相同的cv不合格類型一個參考(12。2) 將被複制到具有相同的CV-不合格類型的類對象時,複製 操作可以通過構建節奏-郭寶宏對象 直接進入省略拷貝

第二種情況的目標可以省略這是我們在這裏。

+1

這不是一個賦值;這是一個初始化。 –

8

您已經使用所謂的「拷貝初始化」(在[decl.init]定義)。所述所定義的含義是構建使用int構造類型X的臨時對象,然後使用拷貝構造初始化從臨時x

然而,該標準還允許在這種情況下,所謂的「拷貝構造函數省音」(在[class.copy]定義)的優化。如果應用該優化,則不存在臨時性。 x使用int構造函數構造,就像您編寫所謂的「直接初始化」X x(1);一樣。

爲了讓你不小心寫代碼時,應用優化,編譯但不是當它不是,該標準要求拷貝構造函數必須是偶數,如果它會被省略訪問。因此,構造函數必須是公開的,即使(使用您使用的編譯器和選項)它不會被調用。

在C++ 11層移動的構造都認爲,和有資格獲得省音了。然而這個類X沒有移動構造函數,所以C++ 11和C++ 03在這個例子中是相同的。

+2

簡而言之,語義決定了一個副本,因此程序在優化之前應首先符合語義。 –

相關問題