首先,非常重要的一点:您有两种不同的构造函数。特别是第一个 C(std::initializer_list<int>),称为 initializer-list 构造函数。第二个只是一个普通的用户定义的构造函数。
[dcl.init.list]/p2
一个构造函数是一个 initializer-list 构造函数,如果它的第一个参数是 std::initializer_list<E> 类型或对某些类型 E 的可能有 cv 限定的 std::initializer_list<E> 的引用,并且要么没有其他参数或所有其他参数都有默认参数 (8.3.6)。
在包含一个或多个initializer-clauses 的列表初始化中,initializer-list 构造函数在任何其他构造函数之前被考虑。也就是说,初始化列表构造函数最初是重载决议期间的唯一候选者。
[over.match.list]/p1
当非聚合类类型T 的对象被列表初始化,使得8.5.4 指定根据本节中的规则执行重载解析时,重载解析分两个阶段选择构造函数:
所以对于c1 和c2 的两个声明,候选集仅包含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的所有构造函数,参数列表由元素组成
初始化器列表。
请注意最后措辞的变化。第二阶段的参数列表不再是单个初始化列表,而是声明中使用的括号初始化列表的参数。这意味着我们可以根据初始化列表单独而不是同时评估隐式转换。
在初始化器列表{1, 2}中,两个初始化器子句都可以转换为int,因此整个初始化器子句都可以转换为initializer_list<int>,{3}也是如此。然后通过选择第二个构造函数来解决重载解决方案。
现在让我们关注c2,现在应该很容易了。首先计算初始化列表构造函数,并且使用{ {1}, {2} } 肯定存在从{1} 和{2} 到int 的转换,因此选择了第一个构造函数。