双括号初始化 [英] Double brace initialization

查看:127
本文介绍了双括号初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码应调用哪个构造函数,为什么?

Which constructor should be called in the following code and why?

struct S
{
    int i;
    S() = default;
    S(void *) : i{1} { ; }
};

S s{{}};

如果我使用clang(从主干),则称为第二个.

If I use clang (from trunk), then the second one is called.

如果第二个构造函数被注释掉,则S{{}}仍然是有效的表达式,但是(我认为)在这种情况下会调用默认构造的S{}实例中的move-constructor.

If the second constructor is commented out, then S{{}} is still valid expression, but (I believe) move-constructor from default-constructed instance of S{} is called in the case.

在第一种情况下,为什么转换构造函数优先于默认构造函数?

Why conversion constructor has priority over the default one in the very first case?

S的构造函数进行这种组合的目的是保存它的std::is_trivially_default_constructible_v< S >属性,但应以某种方式对其进行初始化时,除了有限的几种情况外.

The intention of such a combination of the constructors of S is to save its std::is_trivially_default_constructible_v< S > property, except a finite set of cases, when it should be initialized in a certain way.

推荐答案

如果第二个构造函数被注释掉,那么S {{}}仍然是有效的表达式,但是(我确定)在这种情况下会调用S {}的默认构造实例中的move-constructor.

If the second constructor is commented out, then S{{}} is still valid expression, but (I sure) move-constructor from default-constructed instance of S{} is called in the case.

实际上,这不是事实. [dcl.init.list]中的顺序为:

Actually, that's not what happens. The ordering in [dcl.init.list] is:

对象或类型T的引用的列表初始化定义如下:
—如果T是一个聚合类,并且初始化列表具有单个cv U类型的元素,则[...]
—否则,如果T是一个字符数组并且[...]
—否则,如果T是一个聚合,则执行聚合初始化(8.6.1).

List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate class and the initializer list has a single element of type cv U, [...]
— Otherwise, if T is a character array and [...]
— Otherwise, if T is an aggregate, aggregate initialization is performed (8.6.1).

一旦删除了S(void *)构造函数,S就会成为聚合-它没有用户提供的构造函数. S() = default由于某些原因而不被视为用户提供的.从{}进行的聚合初始化将最终对i成员进行值初始化.

Once you remove the S(void *) constructor, S becomes an aggregate - it has no user-provided constructor. S() = default doesn't count as user-provided because reasons. Aggregate initialization from {} will end up value-initializing the i member.

在第一种情况下,为什么转换构造函数优先于默认构造函数?

Why conversion constructor has priority over the default one in the very first case?

在剩下void*的情况下,让我们继续浏览项目符号列表:

With the void* remaining, let's keep going down the bullet list:

-否则,如果初始化列表中没有元素[...]
—否则,如果T是std :: initializer_list的特化,则[...]
—否则,如果T是类类型,则考虑构造函数.列举适用的构造函数 并通过过载解析(13.3、13.3.1.7)选择了最佳的一个.

— Otherwise, if the initializer list has no elements [...]
— Otherwise, if T is a specialization of std::initializer_list, [...]
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).

[over.match.list]为我们提供了一个两阶段的过载解决过程:

[over.match.list] gives us a two-phase overload resolution process:

-最初,候选函数是类T的initializer-list构造函数(8.6.4)和 参数列表由初始化程序列表作为单个参数组成.
—如果未找到可行的initializer-list构造函数,则将再次执行重载解析,其中 候选函数是类T的所有构造函数,参数列表由元素组成 初始化列表的列表.

— Initially, the candidate functions are the initializer-list constructors (8.6.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.

如果初始化器列表中没有元素,而T具有默认构造函数,则省略第一阶段.

If the initializer list has no elements and T has a default constructor, the first phase is omitted.

S没有任何初始化程序列表构造函数,因此我们进入第二个项目符号,并使用{}的参数列表枚举所有构造函数.我们有多个可行的构造函数:

S doesn't have any initializer list constructors, so we go into the second bullet and enumerate all the constructors with the argument list of {}. We have multiple viable constructors:

S(S const& );
S(S&& );
S(void *);

转换顺序在[over.ics.list]中定义:

The conversion sequences are defined in [over.ics.list]:

否则,如果参数是非聚合类X,并且根据13.3.1.7的重载分辨率,则选择一个 X的最佳构造函数C,用于从参数初始值设定项列表执行X类型的对象的初始化:
—如果C不是初始值设定项列表构造函数,并且初始值设定项列表具有cv U类型的单个元素,则[...] —否则,隐式转换序列是用户定义的转换序列,第二个标准转换序列是身份转换.

Otherwise, if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single best constructor C of X to perform the initialization of an object of type X from the argument initializer list:
— If C is not an initializer-list constructor and the initializer list has a single element of type cv U, [...] — Otherwise, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion.

否则,如果参数类型不是类:[...]-如果初始化列表中没有元素,则隐式转换顺序是身份转换.

也就是说,S(S&& )S(S const& )构造函数都是用户定义的转换序列加上身份转换.但是S(void *)只是身份转换.

That is, the S(S&& ) and S(S const& ) constructors are both user-defined conversion sequences plus identity conversion. But S(void *) is just an identity conversion.

但是[over.best.ics]有以下额外规则:

But, [over.best.ics] has this extra rule:

但是,如果目标是
构造函数的第一个参数
—用户定义的转换函数的隐式对象参数
并且构造函数或用户定义的转换函数是
的候选项 — 13.3.1.3,当[...]
— 13.3.1.4、13.3.1.5或13.3.1.6(在所有情况下),或
第二阶段13.3.1.7,当初始值设定项列表恰好具有一个本身就是初始值设定项列表的元素,并且目标是类X的构造函数的第一个参数,并且转换为X或引用(可能具有简历资格)X

However, if the target is
the first parameter of a constructor or
— the implicit object parameter of a user-defined conversion function
and the constructor or user-defined conversion function is a candidate by
— 13.3.1.3, when [...]
— 13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases), or
the second phase of 13.3.1.7 when the initializer list has exactly one element that is itself an initializer list, and the target is the first parameter of a constructor of class X, and the conversion is to X or reference to (possibly cv-qualified) X,

未考虑用户定义的转换顺序.

这不考虑将S(S const&)S(S&& )作为候选对象-正是这种情况-由于[over.match.list]的第二阶段,目标是构造函数的第一个参数,而目标是可能通过cv限定的S的引用,并且这种转换顺序将由用户定义.

This excludes from consideration S(S const&) and S(S&& ) as candidates - they are precisely this case - the target being the first parameter of the constructor as a result of the second phase of [over.match.list] and the target being a reference to possibly cv-qualified S, and such a conversion sequence would be user-defined.

因此,唯一剩下的候选对象是S(void *),因此,它是最可行的候选对象.

Hence, the only remaining candidate is S(void *), so it's trivially the best viable candidate.

这篇关于双括号初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆