struct X { X(const X &); }; struct Y { operator X() const; }; int main() { X{Y{}}; // ?? error }
在n2672之后,经过defect 978修订,13.3.3.1 [over.best.ics]具有:
4 – However,when considering the argument of a constructor or user-defined conversion function that is a candidate […] by 13.3.1.7 […] when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X […],only standard conversion sequences and ellipsis conversion sequences are considered.
这看起来很不正常;它的结果是使用列表初始化强制转换指定转换是非法的:
void f(X); f(Y{}); // OK f(X{Y{}}); // ?? error
据我所知n2640,列表初始化应该能够替换直接初始化和复制初始化的所有用法,但似乎没有办法只使用列表初始化从类型为Y的对象构造类型为X的对象:
X x1(Y{}); // OK X x2 = Y{}; // OK X x3{Y{}}; // ?? error
这是标准的实际意图吗?如果没有,它应该如何阅读或阅读?
解决方法
4 – At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
在defect 84之前,13.3.3.1p4几乎是纯粹的信息:
4 – In the context of an initialization by user-defined conversion (i.e.,when considering the argument of a user-defined conversion function; see 13.3.1.4 [over.match.copy],13.3.1.5 [over.match.conv]),only standard conversion sequences and ellipsis conversion sequences are allowed.
这是因为13.3.1.4第1段子弹2和13.3.1.5p1b1将候选函数限制为S类,产生类型T,其中S是初始化表达式的类类型,T是要初始化的对象的类型,所以没有其他用户定义的转换转换序列的自由度. (13.3.1.4p1b1是另一个问题;见下文).
缺陷84通过限制允许的转换序列来修复auto_ptr漏洞(即auto_ptr< Derived> – > auto_ptr< Base> – > auto_ptr_ref< Base> – > auto_ptr< Base>,通过两个转换函数和转换构造函数)类复制初始化的第二步中构造函数的单个参数(此处为auto_ptr的构造函数< Base>采用auto_ptr_ref< Base>,不允许使用转换函数将其参数从auto_ptr转换为< Base>):
4 – However,when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization,or by 13.3.1.4 [over.match.copy],13.3.1.5 [over.match.conv],or 13.3.1.6 [over.match.ref] in all cases,only standard conversion sequences and ellipsis conversion sequences are allowed.
n2672然后补充:
[…] by 13.3.1.7 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X,[…]
这显然是混淆的,因为13.3.1.3和13.3.1.7中唯一的候选转换是构造函数,而不是转换函数. Defect 978纠正了这个问题:
4 – However,when considering the argument of a constructor or user-defined conversion function […]
这也使13.3.1.4p1b1与12.3p4一致,否则将允许在复制初始化中无限制地应用转换构造函数:
struct S { S(int); }; struct T { T(S); }; void f(T); f(0); // copy-construct T by (convert int to S); error by 12.3p4
问题是13.3.1.7所指的语言是什么意思. X正在复制或移动构造,因此语言不包括应用用户定义的转换来到达其X参数. std :: initializer_list没有转换函数,因此该语言必须适用于其他内容;如果不打算排除转换函数,则必须排除转换构造函数:
struct R {}; struct S { S(R); }; struct T { T(const T &); T(S); }; void f(T); void g(R r) { f({r}); }
列表初始化有两个可用的构造函数; T :: T(const T&)和T :: T(S).通过排除复制构造函数(因为它的参数需要通过用户定义的转换序列进行转换),我们确保只考虑正确的T :: T(S)构造函数.如果没有这种语言,列表初始化将是模糊的.将初始化列表作为单个参数传递的方式类似:
struct U { U(std::initializer_list<int>); }; struct V { V(const V &); V(U); }; void h(V); h({{1,2,3}});
编辑:经历了这一切,我在Johannes Schaub找到a discussion确认了这个分析:
This is intended to factor out the copy constructor for list initialization
because since we are allowed to use nested user defined conversions,we
could always produce an ambiguous second conversion path by first invoking
the copy constructor and then doing the same as we did for the other
conversions.
好的,关闭提交缺陷报告.我打算建议拆分13.3.3.1p4:
4 – However,when considering the argument of a constructor or user-defined conversion function that is a candidate:
- by 13.3.1.3 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization,or
- by 13.3.1.4 [over.match.copy],
only standard conversion sequences and ellipsis conversion sequences are considered; when considering the first argument of a constructor of a class
X
that is a candidate by 13.3.1.7 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element,a user-defined conversion toX
or reference to (possibly cv-qualified)X
is only considered if its user-defined conversion is specified by a conversion function. [Note: because more than one user-defined conversion is allowed in an implicit conversion sequence in the context of list-initialization,this restriction is necessary to ensure that a converting constructor ofX
,called with a single argumenta
that is not of typeX
or a type derived fromX
,is not ambiguous against a constructor ofX
called with a temporaryX
object itself constructed froma
. — end note]