简体   繁体   English

使用空的初始化程序列表直接初始化

[英]Direct initialization with empty initializer list

struct X
{
    X() { std::cout << "default ctor" << std::endl; }

};

int main()
{
    X({});
}

This prints out 打印出来

default ctor

and that makes sense because empty brace value-initializes the object (I think). 这是有道理的,因为空括号值会初始化对象(我认为)。 However, 然而,

struct X
{
    X() { std::cout << "default ctor" << std::endl; }
    X(std::initializer_list<int>) { std::cout << "initializer list" << std::endl; }
};

int main()
{
    X({});
}

For this, I got 为此,我得到了

initializer list

I don't find this behavior so strange, but I'm not fully convinced. 我觉得这种行为并不奇怪,但我并不完全相信。 What is the rule for this? 这有什么规则?

Is this behavior written in some part of the standard? 此行为是否写在标准的某些部分中?

To see what's really going on, declare copy and move constructors, compile in C++14 mode or earlier, and disable copy elision. 要了解实际情况,请声明复制并移动构造函数,在C ++ 14模式或更早的版本下进行编译,并禁用复制省略功能。

Coliru link Coliru连结

Output: 输出:

default ctor
move ctor

In the first snippet, the compiler looks for constructors of X that take a single argument, since you've provided a single argument. 在第一个代码段中,编译器将查找采用单个参数的X构造函数,因为您已经提供了单个参数。 These are the copy and move constructor, X::X(const X&) and X::X(X&&) , which the compiler will implicitly declare for you if you do not declare them yourself. 这些是复制和移动构造函数X::X(const X&)X::X(X&&) ,如果您自己没有声明它们,则编译器将为您隐式声明它们。 The compiler then converts {} to an X object using the default constructor, and passes that X object to the move constructor. 然后,编译器使用默认构造函数将{}转换为X对象,并将该X对象传递给move构造函数。 (You must use fno-elide-constructors to see this otherwise the compiler will elide the move, and in C++17 copy elision became mandatory.) (您必须使用fno-elide-constructors来查看此内容,否则编译器将忽略此举,并且在C ++ 17中必须执行复制省略。)

In the second snippet, the compiler now has a choice of converting {} to X (then calling the move constructor), or converting {} to std::initializer_list<int> (then calling the initializer list constructor). 在第二个片段中,编译器现在可以选择将{}转换为X (然后调用move构造函数),或者将{}转换为std::initializer_list<int> (然后调用初始化程序列表构造函数)。 According to [over.ics.list]/6.2, the conversion from {} to X , which calls the default constructor, is a user-defined conversion, while according to [over.ics.list]/4, the conversion from {} to std::initializer_list<int> is the identity conversion. 根据[over.ics.list] /6.2,从{}X的转换{}称为默认构造函数)是用户定义的转换,而根据[over.ics.list] / 4,则从{}std::initializer_list<int>是身份转换。 The identity conversion is better than a user-defined conversion, so the compiler calls the initializer list constructor. 身份转换比用户定义的转换要好,因此编译器将调用初始化程序列表构造函数。

Is this behavior written in some part of the standard? 此行为是否写在标准的某些部分中?

Of course. 当然。 It's all dictated by the rules in [dcl.init]/16 , emphasis mine to match your initializer: 所有这些都由[dcl.init] / 16中的规则决定,强调我的内容以匹配您的初始化程序:

The semantics of initializers are as follows. 初始化程序的语义如下。 The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. 目标类型是要初始化的对象或引用的类型,源类型是初始化器表达式的类型。 If the initializer is not a single (possibly parenthesized) expression, the source type is not defined. 如果初始化程序不是单个(可能带有括号)表达式,则不会定义源类型。

  • If the initializer is a ( non-parenthesized ) braced-init-list, the object or reference is list-initialized ([dcl.init.list]). 如果初始化程序是一个( 非括号括起来的 )braced-init-list,则该对象或引用将进行列表初始化([dcl.init.list])。

  • [...] [...]

  • If the destination type is a (possibly cv-qualified) class type: 如果目标类型是(可能是cv限定的)类类型:

    • If the initialization is direct-initialization , or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered . 如果初始化是直接初始化 ,或者是复制初始化,其中源类型的cv不合格版本与目标的类相同,或者是该类的派生类, 则考虑构造函数 The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution ([over.match]). 枚举适用的构造函数([over.match.ctor]),并通过重载分辨率([over.match])选择最佳的构造函数。 The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). 如此选择的构造函数将以初始化器表达式或expression-list作为其自变量来初始化对象。 If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed. 如果没有构造函数适用,或者重载解决方案不明确,则初始化格式错误。
    • [...] [...]

You supply a parenthesized empty brace-init-list, so only the later bullet applies. 您提供了一个带括号的空括号初始列表,因此仅适用于后面的项目符号。 Constructors are considered, and in the first case we end up doing a copy-initialization from a default initialized X . 考虑构造函数,在第一种情况下,我们最终从默认的初始化X进行了复制初始化。 In the latter case, the initializer_list c'tor is chosen as a better match. 在后一种情况下,将initializer_list c'tor选择为更好的匹配。 The rule for choosing this overload is specified in [over.ics.list] : 选择此重载的规则在[over.ics.list]中指定:

When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type. 当参数是初始化程序列表([dcl.init.list])时,它不是表达式,并且特殊规则适用于将其转换为参数类型。

If the parameter type is std::initializer_list or “array of 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. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. 如果参数类型为std :: initializer_list或“ X的数组”,并且初始化列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表的元素转换为X所需的最差转换。即使在调用initializer-list构造函数的上下文中,转换也可以是用户定义的转换。

Otherwise, if the parameter is a non-aggregate class X and overload resolution per [over.match.list] chooses a single best constructor of X to perform the initialization of an object of type X from the argument initializer list, the implicit conversion sequence is a user-defined conversion sequence. 否则,如果参数是非聚合类X,并且每个[over.match.list]的重载分辨率都从参数初始值设定项列表中选择一个最佳的X构造函数来执行类型X对象的初始化,则隐式转换序列是用户定义的转换序列。 If multiple constructors are viable but none is better than the others, the implicit conversion sequence is the ambiguous conversion sequence. 如果多个构造函数可行,但没有一个比其他构造函数更好,则隐式转换序列就是模棱两可的转换序列。 User-defined conversions are allowed for conversion of the initializer list elements to the constructor parameter types except as noted in [over.best.ics]. 除非[over.best.ics]中有说明,否则允许用户定义的转换将初始化列表元素转换为构造函数参数类型。

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

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