简体   繁体   English

为什么使用带括号的初始化程序列表时首选std :: initializer_list构造函数?

[英]Why is the std::initializer_list constructor preferred when using a braced initializer list?

Consider the code 考虑代码

#include <iostream>

class Foo
{
    int val_;
public:
    Foo(std::initializer_list<Foo> il)
    {
        std::cout << "initializer_list ctor" << std::endl;
    }
    /* explicit */ Foo(int val): val_(val)
    {
        std::cout << "ctor" << std::endl;
    };
};

int main(int argc, char const *argv[])
{
    // why is the initializer_list ctor invoked?
    Foo foo {10}; 
}

The output is 输出是

ctor
initializer_list ctor

As far as I understand, the value 10 is implicitly converted to a Foo (first ctor output), then the initializer constructor kicks in (second initializer_list ctor output). 据我了解,值10隐式转换为Foo (第一个ctor输出),然后初始化构造函数启动(第二个initializer_list ctor输出)。 My question is why is this happening? 我的问题是为什么会这样? Isn't the standard constructor Foo(int) a better match? 标准构造函数Foo(int)更好吗? Ie, I would have expected the output of this snippet to be just ctor . 即,我希望该代码段的输出仅为ctor

PS: If I mark the constructor Foo(int) as explicit , then Foo(int) is the only constructor invoked, as the integer 10 cannot now be implicitly converted to a Foo . PS:如果我将构造函数Foo(int)标记为explicit ,则Foo(int)是唯一调用的构造函数,因为现在无法将整数10隐式转换为Foo

§13.3.1.7 [over.match.list]/p1: §13.3.1.7[over.match.list] / p1:

When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases: 当非聚合类类型T被列表初始化(8.5.4)时,重载解析会分两个阶段选择构造函数:

  • 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. 最初,候选函数是类T的初始化器列表构造函数(8.5.4),参数列表由作为单个参数的初始化器列表组成。
  • 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. 如果找不到可行的初始化器列表构造函数,则再次执行重载解析,其中候选函数是T类的所有构造器,并且参数列表由初始化器列表的元素组成。

If the initializer list has no elements and T has a default constructor, the first phase is omitted. 如果初始化列表中没有元素,并且T具有默认构造函数,则省略第一阶段。 In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. 在复制列表初始化中,如果选择了explicit构造函数,则初始化的格式不正确。

As long as there is a viable initializer-list constructor, it will trump all non-initializer-list constructors when list-initialization is used and the initializer list has at least one element. 只要有可行的initializer-list构造函数,当使用list-initialization并且initializer列表至少包含一个元素时,它将胜过所有非initializer-list构造函数。

The n2100 proposal for initializer lists goes into great detail about the decision to make sequence constructors (what they call constructors that take std::initializer_lists ) to have priority over regular constructors. 关于初始化程序列表的n2100建议书详细说明了使序列构造函数(它们称为采用std::initializer_lists构造std::initializer_lists )优先于常规构造函数的决定。 See Appendix B for a detailed discussion. 有关详细讨论,请参见附录B。 It's succinctly summarized in the conclusion: 结论中总结如下:

11.4 Conclusion 11.4结论

So, how do we decide between the remaining two alternatives (“ambiguity” and “sequence constructors take priority over ordinary constructors)? 那么,我们如何在其余两个选择之间做出决定(“歧义”和“序列构造函数优先于普通构造函数”)? Our proposal gives sequence constructors priority because 我们的建议优先考虑序列构造函数,因为

  • Looking for ambiguities among all the constructors leads to too many “false positives”; 在所有构造函数中寻找歧义会导致过多的“误报”; that is, clashes between apparently unrelated constructors. 也就是说,显然无关的构造函数之间发生冲突。 See examples below. 请参阅下面的示例。
  • Disambiguation is itself error-prone (as well as verbose). 消歧本身就容易出错(以及冗长)。 See examples in §11.3. 请参阅第11.3节中的示例。
  • Using exactly the same syntax for every number of elements of a homogeneous list is important – disambiguation should be done for ordinary constructors (that do not have a regular pattern of arguments). 对同类列表中的每个元素使用完全相同的语法非常重要–应该对普通构造函数(没有规则的参数模式)进行歧义消除。 See examples in §11.3. 请参阅第11.3节中的示例。 The simplest example of a false positive is the default constructor: 误报的最简单示例是默认构造函数:

The simplest example of a false positive is the default constructor: 误报的最简单示例是默认构造函数:

 vector<int> v; vector<int> v { }; // potentially ambiguous void f(vector<int>&); // ... f({ }); // potentially ambiguous 

It is possible to think of classes where initialization with no members is semantically distinct from default initialization, but we wouldn't complicate the language to provide better support for those cases than for the more common case where they are semantically the same. 可以考虑没有成员的初始化在语义上与默认初始化不同的类,但是与那些在语义上相同的更为常见的情况相比,我们不会使该语言复杂化以为这些情况提供更好的支持。

Giving priority to sequence constructors breaks argument checking into more comprehensible chunks and gives better locality. 优先考虑序列构造函数,可以将参数检查分成更多可理解的块,并提供更好的局部性。

 void f(const vector<double>&); // ... struct X { X(int); /* ... */ }; void f(X); // ... f(1); // call f(X); vector's constructor is explicit f({1}); // potentially ambiguous: X or vector? f({1,2}); // potentially ambiguous: 1 or 2 elements of vector 

Here, giving priority to sequence constructors eliminates the interference from X. Picking X for f(1) is a variant of the problem with explicit shown in §3.3. 在这里,优先考虑序列构造函数可以消除X的干扰。为f(1)选择X是该问题的一种变体,如第3.3节所示。

The whole initializer list thing was meant to enable list initialisation like so: 整个初始化程序列表是用来启用列表初始化的,如下所示:

std::vector<int> v { 0, 1, 2 };

Consider the case 考虑一下情况

std::vector<int> v { 123 };

That this initializes the vector with one element of value 123 rather than 123 elements of value zero is intended. 这样做是为了用一个值为123的元素而不是值为零的123元素初始化向量。

To access the other constructor, use the old syntax 要访问其他构造函数,请使用旧语法

Foo foo(10);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM