为什么大括号中的标量不作为initializer_list进行处理

时间:2015-07-03 16:27:06

标签: c++ language-lawyer 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;
}

here演示。

为什么c2变量的大括号中的标量值不会被解释为单独的std :: initializer_list?

2 个答案:

答案 0 :(得分:4)

首先,非常重要的事情:你有两种不同的构造函数。第一个特别是C(std::initializer_list<int>),称为初始化列表构造函数。第二个是普通的用户定义构造函数。

[dcl.init.list] / P2

  

构造函数是初始化列表构造函数,如果其第一个参数类型为std::initializer_list<E>,或者对某些类型std::initializer_list<E>可能引用cv限定E ,并且没有其他参数,或者所有其他参数都有默认参数(8.3.6)。

在包含一个或多个 initializer-clauses 的列表初始化中,初始化列表构造函数在任何其他构造函数之前被考虑。也就是说,初始化列表构造函数最初是重载解析期间的唯一候选者。

[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}格式不正确)。这意味着违反了上述报价的条件。由于没有转换,重载解析失败,因为没有其他可行的构造函数,我们被带回[over.match.list] / p1的第二阶段:

  
      
  • 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由元素组成   初始化列表。
  •   

注意最后措辞的变化。第二阶段中的参数列表不再是单个初始化列表,而是声明中使用的braced-init-list的参数。这意味着我们可以根据初始化列表单独而不是同时评估隐式转换。

在initializer-list {1, 2}中,两个initializer-clause都可以转换为int,因此整个initializer-clause可以转换为initializer_list<int>,{{1} }}。然后在选择第二个构造函数的情况下解决重载分辨率。

现在让我们关注{3},现在应该很容易。首先评估初始化列表构造函数,并且使用c2肯定存在从{ {1}, {2} }int{1}的转换,因此选择了第一个构造函数。

答案 1 :(得分:0)

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

这一行不传递std::initializer_list<int>的两个参数,而是传递一个std::initializer_list<std::initializer_list<int> >。一个解决方案是改为实例化c2,如下所示:

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