列表初始化是隐式转换吗?

Sil*_*nic 7 c++ language-lawyer c++11 list-initialization

#include <iostream>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <map>
#include <vector>

class Base{
public:
    virtual ~Base() {}

};

class Derived: public Base { };

int main(){

    int arr[10];
    Derived d;
    Base *p = &d;

    std::map<std::type_index, std::string> proper_name = {
        {typeid(int), "int"}, {typeid(double), "double"}, {typeid(float), "float"}, {typeid(char), "char"},
        {typeid(Base), "Base"}, {typeid(Derived), "Derived"}, {typeid(std::string), "String"},
        {typeid(int[10]), "Ten int Array"}, {typeid(p), "Base Pointer"}};

}
Run Code Online (Sandbox Code Playgroud)

我试图弄清楚这个列表初始化中发生的隐式转换.来自13.3.1.7N3337:

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

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

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

8.5.4:

构造函数是初始化列表构造函数,如果它的第一个参数是类型std::initializer_list<E>或引用,可能std::initializer_list<E>是某些类型的cv-qualified E,并且没有其他参数或者所有其他参数都有默认参数

所以下面的构造函数列表用于std::map指示

map (initializer_list<value_type> il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());

是候选函数,value_type在这种情况下是pair<const type_index, std::string>.最后来自13.3.3.1.5:

如果参数类型是std::initializer_list<X>或"数组X"135并且初始化程序列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表元素转换为所需的最差转换X.

只要被括号列表的元素隐式转换为,它就是一个有效的转换pair<const type_index, std::string>.但这些元素也是支撑列表本身.Pair没有采用初始化列表构造函数,从这里看来,来自braced-init列表的复制初始化使用第二部分13.3.1.7来构造对象.所以以下内容:

pair<const type_index, std::string> p = {typeid(int), "int"}
Run Code Online (Sandbox Code Playgroud)

变为:

pair<const type_index, std::string> p(typeid(int), "int")
Run Code Online (Sandbox Code Playgroud)

但这被认为是一种隐含的转换吗?如何使用双参数构造函数作为隐式转换?标准对此有何评论?

Pra*_*ian 3

你的结论是

pair<const type_index, std::string> p = {typeid(int), "int"};
Run Code Online (Sandbox Code Playgroud)

变成

pair<const type_index, std::string> p(typeid(int), "int");
Run Code Online (Sandbox Code Playgroud)

不准确,因为第一个语句是copy-list-initialization而第二个语句是direct-initialization。两者是相同的,只是如果选择构造函数,则复制列表初始化是格式错误的explicit(并且前者不允许缩小转换)。

因此,如果pair有问题的构造函数定义为

template<class U1, class U2>
explicit constexpr pair(U1&& x, U2&& y);
Run Code Online (Sandbox Code Playgroud)

直接初始化仍然会成功,但复制列表初始化会失败。从正下方引用您引用的[over.match.list]部分

在复制列表初始化中,如果explicit选择构造函数,则初始化格式错误。


除此之外,你说的其他一切都是正确的。构造pair函数是隐式转换,因为构造函数不是隐式转换,并且根据[over.match.list]explicit的第二个项目符号考虑重载解析,因为它没有初始值设定项列表构造函数。pair

  • Nitpick:“两者是相同的,除了如果选择显式构造函数,则复制列表初始化格式不正确”,[除非初始值设定项列表具有类型相同或从对象类型派生的单个元素正在初始化](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1467)(如果[有一个initializer_list构造函数](可能会得到异常的异常)( http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2137))。 (2认同)