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

Is list-initialization an implicit conversion?

#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"}};

}

我正在尝试理解此列表初始化中发生的隐式转换。来自 N3337 的 13.3.1.7

When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:

  1. Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.

  2. If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

8.5.4:

A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list<E> or reference to possibly cv-qualified std::initializer_list<E> for some type E, and either there are no other parameters or else all other parameters have default arguments

所以 the following 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

If the parameter type is std::initializer_list<X> or “array of X”135 and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X.

因此,只要花括号列表的元素隐式转换为 pair<const type_index, std::string>,它就是一个有效的转换。但这些元素本身也是花括号列表。 Pair 不采用初始化列表构造函数,from here 似乎来自花括号初始化列表的复制初始化使用 13.3.1.7 的第二部分来构造对象。所以如下:

pair<const type_index, std::string> p = {typeid(int), "int"}

变为:

pair<const type_index, std::string> p(typeid(int), "int")

但这是否被视为隐式转换?如何将双参数构造函数的使用视为隐式转换?标准对此有何评论?

你的结论是

pair<const type_index, std::string> p = {typeid(int), "int"};

变成

pair<const type_index, std::string> p(typeid(int), "int");

不准确,因为第一个语句是 copy-list-initialization,而第二个语句是 direct-initialization。两者是相同的,除了 copy-list-initialization 是 ill-formed 如果选择 explicit 构造函数(并且前者不允许缩小转换)。

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

template<class U1, class U2>
explicit constexpr pair(U1&& x, U2&& y);

direct-initialization 仍然会成功,但 copy-list-initialization 会失败。从 [over.match.list] 部分的正下方引用你引用的

In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.


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