#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"}};
}
I'm trying to make sense of the implicit conversions that occur in this list-initialization. From 13.3.1.7
of N3337:
When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
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.
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-qualifiedstd::initializer_list<E>
for some typeE
, and either there are no other parameters or else all other parameters have default arguments
So the following list of constructors for std::map
indicates
map (initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type())
;
is the candidate function, value_type
in this case is pair<const type_index, std::string>
. Lastly from 13.3.3.1.5
:
If the parameter type is
std::initializer_list<X>
or “array ofX
”135 and all the elements of the initializer list can be implicitly converted toX
, the implicit conversion sequence is the worst conversion necessary to convert an element of the list toX
.
So it is a valid conversion as long as the elements of the braced-list implicitly convert to pair<const type_index, std::string>
. But those elements are also braced-lists themselves. Pair
does not take an initializer-list constructor, from here it seems that copy-initialization from a braced-init list uses the second part of 13.3.1.7
to construct the object. So the following:
pair<const type_index, std::string> p = {typeid(int), "int"}
becomes:
pair<const type_index, std::string> p(typeid(int), "int")
but is this considered an implicit conversion? How can use of a two-argument constructor be considered an implicit conversion? What are the standard's comments on this?