2017-10-08 111 views
0

在C++中初始化對象(類或結構的實例)可以通過多種方式完成。有些語法會引起對象的直接初始化,其他語法會導致複製初始化。在編譯器中啓用了copy-elision,兩者的性能相同。如果copy-elision已禁用,則在爲後者(複製初始化)選擇時,每個實例化都會有一個額外的複製/移動構造函數調用。它是直接初始化還是複製初始化?

結論:複製初始化可能會有性能損失!

從以下問題:C++11 member initializer list vs in-class initializer?我可以斷定,這將是副本初始化語法:

obj s = obj("value"); 

,這將是直接初始化語法:

obj s{"value"}; 

 
但是這個怎麼樣:

obj s = {"value"}; 

這一個:

obj s = obj{"value"}; 

這一個:

obj s("value"); 

或者這一個:

obj s = "value"; 

注意
了Bjarne Stroustrup的在他的著作 「編程,原理與實踐使用C++」 第二版,比較少數的初始化樣式(但不是全部)311頁,§9.4.2:

struct Date { 
    int y,m,d;      //year, month, day 
    Date(int y, int m, int d);  //check for valid date and initialize 
    void add_day(int n);   //increase the Date by n days 
}; 

...

Date my_birthday;     //error: my_birthday not initialized 
Date today{12,24,2007};    //oops! run-time error 
Date last{2000,12,31};    //OK (colloquial style) 
Date next = {2014,2,14};   //also OK (slightly verbose) 
Date christmas = Date{1976,12,24}; //also OK (verbose style) 

斯特勞斯先生介紹了這些不同的初始化風格相等。至少,這就是它對我的看法。儘管如此,仍有可能有一些是直接初始化和其他複製初始化,因爲這些條款尚未在書中討論。


編輯
給出的答案帶來了一些有趣的事情。
顯然,這是直接初始化

obj s("value"); 

這是直接列表初始化

obj s{"value"}; 

你們有些人指出,是有區別的。他們實際上以何種方式不同?在非優化編譯器的輸出中,差異是否會顯着?

+0

有沒有'='?是的:這是複製初始化。否則是直接初始化。 – Rakete1111

+0

@ Rakete1111 [這似乎不是真的](http://coliru.stacked-crooked.com/a/18e4e0f6db8a9bfc)。 – nwp

+0

@nwp當然,如果你使用大括號,它是* -list-initialization,取決於'='。對不起,謝謝! – Rakete1111

回答

3
obj s = obj("value"); 

這是一個prvalue,然後將其用於複製初始化變量s的直接初始化。 C++ 17的prvalue規則使這個事實上的直接初始化爲s

obj s{"value"}; 

這是指示按列表 -initialization。 「列表」部分很重要。任何時候,爲了初始化對象而應用braced-init-list,您都在執行列表初始化。

obj s = {"value"}; 

這是副本列表初始化。

obj s = obj{"value"}; 

這是一個prvalue,然後將其用於複製初始化變量s的直接清單初始化。

obj s("value"); 

即直接初始化。

obj s = "value"; 

這是拷貝初始化。

Stroustrup先生將這些不同的初始化樣式顯示爲相等。

他們在大致相同的意義上是平等的。但他們在技術上並不相同;複製列表初始化不能調用explicit構造函數。因此,如果選定的構造函數是explicit,代碼將無法在複製列表初始化情況下編譯。

+1

從技術上講,[direct-initialization](https://wg21.link/dcl.init#16)包括直接列表初始化。這就是爲什麼L(E)WG發明[「direct-non-list-initialization」](http://eel.is/c++draft/defns.direct-non-list-init)來描述由'可選「和朋友。 –

+0

非常感謝。請在我的問題中查看**編輯**。我想知道更多關於**直接初始化**和**直接列表初始化**之間的區別** :-) –

+0

@ K.Mulier:查看「列表初始化」。這是主要區別。 –

2

一般來說:

複製初始化,右手側被隱式轉換爲類型T的臨時實例,從中012隨後複製/移動構建。

Mr. Stroustrup先生將這些不同的初始化樣式表示爲相等。

在許多情況下,生成(優化)的代碼確實是完全一樣的。編譯器允許複製構造elide(即使它有副作用)。現代編譯器不僅僅是簡單的優化,比如這個,所以你可以有效地指望這個elision(這在C++ 17中是必需的)。

複製和直接初始化之間的區別仍然非常重要,因爲語義是不同的;例如,調用構造函數explicit只能在直接初始化


形式T s = {...};copy-list-initialization並遵循一些特殊的列表初始化規則。

+0

雖然有區別。對於複製列表初始化,沒有臨時的。 – Rakete1111

+1

@ Rakete1111 - true,但在* copy-list-initialization *和* direct-list-initialization之間仍然存在[difference](http://eel.is/c++draft/dcl.init#list-3.2) *。 – rustyx

+0

非常感謝@RustyX。請在我的問題中看看編輯。我想知道更多關於直接初始化和直接列表初始化之間的區別: - –

2

您可以很容易地look up這些問題的答案。也就是說,簡單的答案是=意味着複製初始化。然而,T t={...};複製列表初始化,其中(除非大括號只包含一個T或從中派生的東西)不涉及副本!但是,它確實不允許使用非構造函數。