为什么C++允许将std :: initializer_list强制转换为原始类型,并用于初始化它们?

zhi*_*ang 9 c++ initializer-list c++11

这个问题与std :: initializer_list有关,以及为什么允许初始化基本类型.考虑以下两个功能:

void foo(std::string arg1, bool arg2 = false);
void foo(std::string arg1, std::deque<std::string> arg2, bool arg3 = false);
Run Code Online (Sandbox Code Playgroud)

为什么在这样调用foo时:

foo("some string", { });
Run Code Online (Sandbox Code Playgroud)

选择第一个重载,而不是第二个?好吧,实际上不是为什么它被选中,这是因为{ }可以用来初始化任何东西,包括原始类型.我的问题是这背后的原因.

std :: initializer_list采用{ args... },因此在编译时不能具有不确定的长度.试图这样做bool b = { true, true }给人error: scalar object 'b' requires one element in initialiser.

尽管允许统一初始化似乎是一个好主意,但事实是这是令人困惑且完全出乎意料的行为.确实,编译器如何才能做到这一点,在后台做一些魔术没有做std :: initializer_list的事情?

除非{ args... }是C++词法结构,否则我的观点仍然存在:为什么它允许用于原始类型的初始化?

谢谢.在意识到错误的重载被调用之前,我在这里遇到了很多错误.用了10分钟搞清楚原因.

Pio*_*cki 5

{}语法是一个braced-init-list,因为它在函数调用中用作参数,所以copy-list-initialize一个相应的参数.

§8.5[dcl.init]/p17:

(17.1) - 如果初始化器是(非括号的)braced-init-list,则对象或引用进行列表初始化(8.5.4).

§8.5.4[dcl.init.list]/p1:

列表初始化是从braced-init-list初始化对象或引用.这样的初始化程序称为初始化程序列表,列表的逗号分隔的初始化程序子句称为初始化程序列表的元素.初始化列表可以为空.列表初始化可以在直接初始化复制初始化上下文中进行; [...]

对于具有列表初始化的类类型参数,重载决策分两个阶段查找可行的构造函数:

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

当非聚合类类型的对象T被列表初始化(8.5.4)时,重载决策分两个阶段选择构造函数:

- 最初,候选函数是类的初始化列表构造函数(8.5.4),T参数列表由初始化列表作为单个参数组成.

- 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类的所有构造函数,T参数列表由初始化列表的元素组成.

但:

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

由于std::deque<T>定义了非显式默认构造函数,因此可以将其中一个添加到一组可行的函数中以进行重载解析.通过构造函数初始化被归类为用户定义的转换(第13.3.3.1.5节[over.ics.list]/p4):

否则,如果参数是非聚合类,X并且每个13.3.1.7的重载决策选择单个最佳构造函数XX从参数初始化列表执行类型对象的初始化,则隐式转换序列是用户定义的转换序列用第二标准转换序列进行身份转换.

更进一步,空的braced-init-list可以初始化其相应的参数(第8.5.4节[dcl.init.list]/p3),对于文字类型代表零初始化:

(3.7) - 否则,如果初始化列表没有元素,则对象进行值初始化.

对于像这样的文字类型bool,这不需要任何转换,并且被归类为标准转换(第13.3.3.1.5节[over.ics.list]/p7):

否则,如果参数类型不是类:

(7.2) - 如果初始化列表没有元素,则隐式转换序列是标识转换.

[ 例如:

void f(int);
f( { } );
// OK: identity conversion
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]

如果存在对应参数的转换序列优于另一个重载的转换序列(第13.3.3节[over.match.best]/p1),则首先进行重载分辨率检查:

[...]鉴于这些定义,如果对于所有参数,可行函数F1被定义为比另一个可行函数更好的函数,不是比 转换序列更差,然后:F2iICSi(F1)ICSi(F2)

(1.3) - 对于某些论证j,ICSj(F1)是一个比ICSj(F2)[或] 更好的转换序列,或者,如果不是,[...]

转换序列按照§13.3.3.2[over.ics.rank]/p2进行排名:

比较隐式转换序列的基本形式(如13.3.3.1中所定义)

(2.1) - 标准转换序列(13.3.3.1.1)是比用户定义的转换序列或省略号转换序列更好的转换序列,并且[...]

因此,bool初始化{}为的第一个重载被认为是更好的匹配.