c++ - 为什么大括号中的标量不被解释为 initializer_list

标签 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;
}

Live演示。

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

最佳答案

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

[dcl.init.list]/p2

A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list<E> or reference to possibly cv-qualified std::initializer_list<E> for some type E, and either there are no other parameters or else all other parameters have default arguments (8.3.6).

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

[over.match.list]/p1

When objects of non-aggregate class type T are list-initialized such that 8.5.4 specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases:

  • Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.

  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

因此对于 c1 的两个声明和 c2候选集仅包含 C(std::initializer_list<int>)构造函数。

选择构造函数后,将对参数求值以查看是否存在将它们转换为参数类型的隐式转换序列。这使我们了解了初始化列表转换的规则:

[over.ics.list]/p4(强调我的):

Otherwise, if the parameter type is std::initializer_list<X> and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion.

这意味着如果初始化列表的每个元素都可以转换为 int,则存在转换。 .

让我们关注 c1现在:对于初始化列表 {{1, 2}, {3}} , 初始化子句 {3}可以转换为int ([over.ics.list]/p9.1),但不是 {1, 2} (即 int i = {1,2} 格式错误)。这意味着违反了上述报价的条件。由于没有转换,重载决议失败,因为没有其他可行的构造函数,我们被带回到 [over.match.list]/p1 的第二阶段:

  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

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

在初始化列表中 {1, 2} , 两个初始化子句都可以转换为 int , 所以整个初始化子句可以转换为 initializer_list<int> , 与 {3} 相同.然后通过选择第二个构造函数来解决重载问题。

现在让我们关注 c2 ,现在应该很容易了。首先评估初始化列表构造函数,然后使用 { {1}, {2} }肯定存在到 int 的转换来自 {1}{2} , 所以选择了第一个构造函数。

关于c++ - 为什么大括号中的标量不被解释为 initializer_list,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31211021/

相关文章:

c++ - 我如何在 linux 中获取程序的版本

c++ - 如果 void 实际上被定义为 `struct void {};` 会破坏多少现有的 C++ 代码

c++ - 通过 unsigned char 别名访问对象,加载和存储时会发生什么?

constructor - Dart (/flutter ): Create function in initializer list

c++ - 设计着色器类

c++ - 在 C++ dll 中保持 MATLAB 引擎打开

c++ - 使用 boost::variant 时运算符 * 不匹配

c - 将无符号值分配给带符号的字符

c++ - 在构造函数的初始化列表中使用 std::initializer_list 初始化 std::array

c++ - 成员初始化器列表。参数评估顺序