2015-07-03 76 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

相关问题