13.3.3.1p4 的初衷是描述如何应用 12.3p4 中的要求:
4 - 最多一个用户定义的转换(构造函数或转换函数)隐式应用于单个值。
在缺陷 84之前,13.3.3.1p4几乎是纯粹的信息:
4 - 在通过用户定义转换进行初始化的上下文中(即,当考虑用户定义转换函数的参数时;参见 13.3.1.4 [over.match.copy]、13.3.1.5 [over.match.conv] ),只允许标准转换序列和省略号转换序列。
这是因为 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 - 但是,当考虑作为 13.3.1.3 [over.match.ctor] 候选的用户定义转换函数的参数时,当在类复制初始化的第二步中调用以复制临时文件时,或通过 13.3.1.4 [over.match.copy]、13.3.1.5 [over.match.conv] 或 13.3.1.6 [over.match.ref] 在所有情况下,只允许标准转换序列和省略号转换序列。
n2672然后添加:
[...] 由 13.3.1.7 [over.match.list] 当初始化器列表作为单个参数传递或初始化器列表只有一个元素并且转换为某个类 X 或引用(可能是 cv 限定)时X 被认为是 X 的构造函数的第一个参数,[...]
这显然令人困惑,因为 13.3.1.3 和 13.3.1.7 候选的唯一转换是构造函数,而不是转换函数。 缺陷 978纠正了这一点:
4 - 但是,当考虑构造函数或用户定义的转换函数的参数时 [...]
这也使得 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的讨论证实了这一分析:
这旨在为列表初始化排除复制构造函数,因为因为我们被允许使用嵌套的用户定义转换,所以我们总是可以通过首先调用复制构造函数然后像我们对另一个执行相同的操作来生成一个模棱两可的第二个转换路径转换。
好的,关闭提交缺陷报告。我将提议拆分 13.3.3.1p4:
4 - 但是,当考虑作为候选的构造函数或用户定义转换函数的参数时:
- 由 13.3.1.3 [over.match.ctor] 在类复制初始化的第二步中调用以复制临时文件时,或
- 在所有情况下,通过 13.3.1.4 [over.match.copy]、13.3.1.5 [over.match.conv] 或 13.3.1.6 [over.match.ref],
只考虑标准转换序列和省略号转换序列;当考虑作为X
13.3.1.7 [over.match.list] 候选的类的构造函数的第一个参数时,当将初始值设定项列表作为单个参数传递或初始值设定项列表只有一个元素时,用户定义的转换仅当其用户定义的转换由转换函数指定时才考虑toX
或 reference to (可能是cv限定的) 。X
[注意:因为在列表初始化的上下文中,隐式转换序列中允许有多个用户定义的转换,所以这个限制是必要的,以确保X
使用单个参数调用的转换构造函数a
that 不是类型X
或派生自 的类型,对于使用本身构造的临时对象调用X
的构造函数并不模棱两可。——尾注]X
X
a