C++列表初始化允许多个用户定义的转换

C++ list initialization allows multiple user-defined conversions

本文关键字:定义 转换 用户 许多个 列表 初始化 C++      更新时间:2023-10-16

我正在阅读这个答案,它有以下示例:

struct R {};
struct S { S(R); };
struct T { 
T(const T &); //1 
T(S);         //2
};
void f(T);
void g(R r) {
f({r});
}

答案与旧版本的[over.best.ics]/4有关,当时看起来是这样的:

然而,当考虑构造函数或用户定义的转换函数的参数时,当在类复制初始化的第二步中为复制/移动临时对象而调用[over.match.ctor]时,通过[over.match.list]将初始值设定项列表作为单个参数传递时,或者当初始值设定项列表正好有一个元素,并且对某个类X的转换或对X的引用(可能是cv限定的(被考虑作为X的构造函数的第一个参数时,或者在所有情况下通过[over.match.copy]、[over.maatch.conv]或[over.match.ref],仅考虑标准转换序列和省略号转换序列。

在答案中,如果没有上面引用中突出显示的部分,f({r})将是不明确的,因为它可以使用T(1(的第一构造函数或第二构造函数(2(。

然而,尽管我尝试了很多,我还是看不出第一个构造函数(1(是如何成为一个选项的。f({r})导致T{r}复制列表初始化。如果使用第一个构造函数,则标准允许从r转换为构造函数的参数类型。然而,仅仅进行一次转换是不够的,因为必须进行R-->S(使用S的转换构造函数(然后S->T(使用T(2(的转换构造函数(。我在标准中找不到任何允许在列表初始化的强制转换中进行多个用户定义转换的内容。

我可能错过了什么。如果有人指出我的错误,我将不胜感激,如果我没有,我想知道引用该标准中突出部分的目的是什么。

引用段落的当前版本要求初始化器列表的唯一元素是初始化器列表本身,如果不是f({r})而是f({{r}}),这在上面的示例中是有意义的。在这种情况下,解释是正确的。

谢谢。

您的观察是正确的。即使没有强调的部分,这个例子也很好,但原因有点不同。

我在标准中找不到任何允许在列表初始化的强制转换中进行多个用户定义转换的内容。

事实上,[over.best.ics]/4正是禁止多个用户定义转换的规则。考虑调用(1(,则我们必须使用r复制初始化类型为T的临时对象,该对象属于"或在所有情况下由[over.match.copy]、[over.macc.conv]或[over.match.ref]"部分,因此禁止用户定义的转换(r -> const T&r -> S(。因此,我们不能为(1(形成隐式转换序列,因此(2(获胜。

请注意,由于问题1758,强调的部分曾经被删除,并且由于问题2076,再次返回时带有约束"初始值设定项列表只有一个元素本身就是初始值设定值列表"。