2017-03-03 110 views
11

應該在下面的代碼中調用哪個構造函數,爲什麼?雙括號初始化

struct S 
{ 
    int i; 
    S() = default; 
    S(void *) : i{1} { ; } 
}; 

S s{{}}; 

如果使用clang(從主幹),那麼第二個被調用。

如果第二個構造函數被註釋掉了,那麼S{{}}仍然是有效的表達式,但是(我相信)在這種情況下調用了從默認構造的S{}實例中移動構造函數。

爲什麼轉換構造函數在第一種情況下的優先級高於默認值?

S的構造的這種組合的目的是保存其std::is_trivially_default_constructible_v<S>屬性,除了一組有限的情況下,當它應該以某種方式被初始化的。

+0

「*如果第二個構造已被註釋掉,則S {{}}仍然是有效的表達,但(我知道)移動構造函數從第{}被調用的情況下默認構造的實例。*」號它會聚合 - 用'brace'初始化'int'來初始化'S'。 – ildjarn

+0

@ildjarn問題是仍然有效。 – Orient

回答

9

如果第二個構造函數被註釋掉了,那麼S {{}}仍然是有效的表達式,但是在我的情況下調用了S {}的默認構造實例的move-constructor。

實際上,那不會發生什麼。在[dcl.init.list]的順序是:

對象或類型T的參考的列表的初始化被定義如下:
- 如果T是一個聚合類和初始化列表具有單個[...]類型cv U的元素,
- 否則,如果T是一個字符數組,

一旦您刪除S(void *)構造函數,S將成爲聚合 - 它沒有用戶提供的構造函數。由於原因,S() = default不計爲用戶提供。來自{}的總體初始化將最終對i成員進行初始化。


爲什麼轉換構造了一個默認的第一個案件有優先權?

隨着void*剩下的,讓我們繼續下去的項目列表:

- 否則,如果初始化列表中沒有的元素[...]
- 否則,如果T是一個專業化std :: initializer_list,[...]
- 否則,如果T是類類型,則考慮構造函數。列舉的適用構造函數爲 ,最好的是通過重載分辨率(13.3,13.3.1.7)選擇的。

[over.match.list]爲我們提供了兩相重載解析過程:

- 最初,候選功能初始化列表構造器(8.6。4)T和 參數列表由初始化程序列表作爲單個參數組成。
- 如果沒有找到可行的初始化列表構造函數,重載決議再次進行,其中 候選函數是T類的所有構造函數和參數列表由元素初始化列表的 的。

如果初始化列表沒有任何元素,T具有一個默認的構造,省略了第一階段。

S沒有任何初始化列表構造函數,所以我們進入第二個項目符號和枚舉所有的{}參數列表的構造函數。我們有多個可行的構造:

S(S const&); 
S(S&&); 
S(void *); 

轉換序列在[over.ics.list]定義:

否則,如果參數是每13.3非聚合類X和重載解析.1.7選擇X的單個 最好構造C到執行從參數初始化列表X類型的對象的初始化:
- 如果C不是一個初始化列表構造器和初始化列表具有式CV的單個元件U,[...] - 否則,隱式轉換序列是用戶定義的轉換序列與第二個標準轉換序列的標識轉換

否則,如果參數類型是不是一類:[...] - 如果初始化列表沒有任何元素,的隱式轉換的序列是恆等變換

即,S(S&&)S(S const&)構造都是用戶定義的轉換序列加上標識轉換。但S(void *)只是一個身份轉換。

但是,[over.best.ics]有這樣額外的規則:

然而,如果目標是
- 構造
的第一個參數 - 隱含的對象參數用戶定義的轉換函數
和構造或用戶定義的轉換函數是通過
候選 - 13.3.1.3,當[...]
- 13.3.1.4,13.3.1.5,或13.3.1.6(在所有情況下),或
- 的13.3.1.7第二階段時初始化列表具有恰好一個要素即本身初始化值列表,並且目標是X類的構造函數的第一參數,以及轉換是X或參考(可能是cv合格的)X,

不考慮用戶定義的轉換序列。

這從考慮S(S const&)S(S&&)排除作爲候選 - 它們是正是這種情況下 - 目標是所述構造的作爲[over.match.list]第二階段和所述目標的結果的第一參數作爲一個參考,從而可能CV-合格S,並且這樣的轉換序列將是用戶定義的。

因此,剩下的唯一候選人是S(void *),所以它是平凡的最佳可行的候選人。

+0

@ T.C我想我不知道,如果:第二個是一個標準的轉換序列。第一種是明確聲明爲用戶定義的... – Barry

+3

除了一點細節之外,您的分析是正確的:移動和複製構造函數不可行,因爲16.3.3.1 [over.best.ics] p4「以及構造函數或用戶定義的轉換函數是一個候選者,通過... [over.match.list]的第二個階段,當初始化器列表恰好有一個元素本身就是一個初始化器列表,並且目標是類X的構造器的第一個參數,並且轉換爲X或對cv X的引用...不考慮用戶定義的轉換序列。「 –

+0

如果在他移除用戶提供的構造函數時(例如,通過拼寫出default-constructor的定義),它不是一個聚合函數,那麼刪除其轉換構造函數的示例將不再編譯。 –