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

时间:2018-07-31 18:34:40

标签: c++ language-lawyer list-initialization

我正在阅读this answer,其中包含以下示例:

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的旧版本有关,该旧版本看起来像this

  

但是,当在类copy-initialization的第二步中为复制/移动临时对象而调用[over.match.ctor]作为候选的构造函数或用户定义的转换函数的参数时, ,由[over.match.list]通过将初始化器列表作为单个参数传递,或者当初始化器列表仅具有一个元素并且考虑到对某个类X的转换或对X的引用(可能是cv限定)时, X的构造函数的第一个参数,或者在所有情况下都按[over.match.copy],[over.match.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}}),则在上面的示例中这是有意义的。在这种情况下,解释将是正确的。

谢谢。

1 个答案:

答案 0 :(得分:2)

您的观察是正确的。即使没有强调的部分,该示例也格式正确,但是原因有所不同。

  

而且我在标准中找不到任何东西可以在列表初始化的转换中进行多个用户定义的转换。

事实上,[over.best.ics] / 4正是禁止多于一个用户定义的转换的规则。考虑到调用了(1),那么我们必须用puts "<table>" CSV.foreach("path/to/file.csv") do |row| puts "<tr>" # print out all data in row here... puts "</tr>" end puts "</table>" 复制初始化类型T的临时对象,该对象属于“或[over.match.copy],[over.match]。 conv]或[over.match.ref]”部分,因此禁止用户定义的转换(rr -> const T&)。结果,我们无法为(1)形成隐式转换序列,因此(2)获胜。

请注意,强调的部分由于issue 1758而被删除,并且由于issue 2076而再次受到约束“初始化器列表中只有一个元素本身就是初始化器列表”。