2015-07-03 36 views
6

考慮下面的代碼片段:爲什麼在大括號標不interpeted爲initializer_list

#include <iostream> 
#include <initializer_list> 

struct C 
{ 
    C(std::initializer_list<int>) { std::cout << "list\n"; } 
    C(std::initializer_list<int>, std::initializer_list<int>) { std::cout << "twice-list\n"; } 
}; 

int main() 
{ 
    C c1 { {1,2}, {3} }; // twice-list ctor 
    C c2 { {1}, {2} }; // why not twice-list ? 
    return 0; 
} 

Live演示。

爲什麼變量的大括號c2的標量值不會被解釋爲單獨的std :: initializer_list?

回答

4

首先,很重要的事:你有兩種不同類型的構造函數。第一個特別是C(std::initializer_list<int>),被稱爲構造函數的初始化列表。第二個只是一個普通的用戶定義的構造函數。

[dcl.init.list]/P2

構造函數是一個初始化列表構造如果第一個參數是std::initializer_list<E>型或參照可能CV-合格某種類型Estd::initializer_list<E>的,並且沒有其他參數,否則所有其他參數都有默認參數(8.3.6)。

在一個含有一個或多個初始化子句列表初始化,初始值設定列表構造器被任何其他構造之前考慮。也就是說,初始化列表構造函數最初是重載解析期間唯一的候選項。

[over.match.list]/P1

當非聚合類類型T的對象是列表初始化使得8.5.4指定重載解析是根據本節中的規則進行,過載分辨率在兩個階段中選擇的構造:

  • 最初,候選函數是類T和參數列表的初始化列表構造器(8.5.4)由初始化列表作爲單個參數的。

  • 如果沒有找到可行的初始化列表構造函數,重載決議再次進行,其中候選功能類T的所有構造函數和參數列表由元素初始化列表的 的。

所以對於c1c2雙方的聲明候選集僅包含的C(std::initializer_list<int>)構造。

構造之後被選擇的參數進行評估,以查看是否存在的隱式轉換序列將它們轉換爲參數類型。這需要我們爲初始化列表轉換規則:

[over.ics.list]/P4(重點煤礦):

否則,如果該參數的類型是std::initializer_list<X>和的所有元素初始化列表可以隱式轉換到X,隱式轉換序列是必要的列表的一個 元件轉換爲X最壞轉換,或者如果初始化列表沒有任何元素,身份轉換。

這意味着如果初始化程序列表中的每個元素都可以轉換爲int,則存在轉換。

讓我們專注於c1現在:對於初始化列表{{1, 2}, {3}},初始化子句{3}可以轉換爲int([over.ics.list] /p9.1),但不{1, 2}(即int i = {1,2}病-formed)。這意味着違反了上述報價的條件。

  • 如果沒有可行的初始化列表:由於沒有轉換,重載因爲沒有其他可行的構造,我們帶回的[over.match.list]/P1的第二階段失敗找到構造函數,再次執行重載解析,其中候選函數是類T的所有構造函數,參數列表包含初始化程序列表的元素 。

注意最後措辭的變化。第二階段的參數列表不再是一個初始化列表,而是聲明中使用的braced-init-list的參數。這意味着我們可以根據初始化程序列表單獨而不是同時評估隱式轉換。

在初始化列表{1, 2},無論初始值設定子句可以轉換爲int,所以整個初始化子句可以轉換爲initializer_list<int>,同樣爲{3}。然後選擇第二個構造函數解析重載解析。

現在,讓我們專注於c2,現在這應該很容易。初始化列表構造首先計算,並且,使用{ {1}, {2} }有可靠地存在於從{1}{2}轉換到int,所以選擇所述第一構造函數。

+0

因此,爲什麼如果我將'C(std :: initializer_list ,std :: initializer_list )'和'c2'聲明更改爲'C c2 {{1.0},{2}};'我會得到編譯器錯誤?根據你的回答,必須爲此選擇第二個構造函數。或者我做了錯誤的結論? [現場演示](http://coliru.stacked-crooked.com/a/e299e66462e220cf) – alexolut

+1

@alexolut [over.isc.list]/p4:「[...]和初始化程序列表中的所有元素可以爲 被隱含地轉換爲'X',「一個'double'可以被隱式轉換爲'int',所以這個條款成立。進一步閱讀:「隱式轉換序列是將列表元素 轉換爲」X「所需的最差轉換。這個隱式轉換序列是*縮小轉換序列*。所以並不是沒有找到一個可行的初始化列表構造函數(進入階段2意味着我們還沒有找到一個),而是轉換本身導致程序不合格。 – 0x499602D2

+0

即,找到可行的構造函數,但自變量本身不可行(不合格)。在這種情況下,我們沒有達到第二階段。 - 我的理解正確嗎? – alexolut

0

​​

此行不會在std::initializer_list<int>兩個參數傳遞,而是,它是在傳遞一個std::initializer_list<std::initializer_list<int> >。一個解決辦法是代替實例c2像這樣:

C c2({1}, {2});

+1

問題是「爲什麼」。 –

+0

我明白了。讓我快速獲取參考並編輯。 – ross

+0

基本上,「爲什麼」的答案是因爲當std :: initializer_list出現在構造函數中時它具有特殊的優先級。如果你有一個帶std :: initializer_list的構造函數,並且使用大括號初始化來構造對象,那麼編譯器會盡力將你輸入的內容轉換爲適合std :: initializer_list的構造函數,即使它不是最合適的 – KABoissonneault

相關問題