#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?
The initializer list is used to directly initialize data members of a class. An initializer list starts after the constructor name and its parameters.
Conclusion: All other things being equal, your code will run faster if you use initialization lists rather than assignment.
An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type const T .
An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.
Your conclusion that
pair<const type_index, std::string> p = {typeid(int), "int"};
becomes
pair<const type_index, std::string> p(typeid(int), "int");
is not accurate because the first statement is copy-list-initialization while the second is direct-initialization. The two are identical, except that copy-list-initialization is ill-formed if an explicit
constructor is chosen (and narrowing conversions are not allowed in the former).
So if the pair
constructor in question was defined as
template<class U1, class U2>
explicit constexpr pair(U1&& x, U2&& y);
direct-initialization would still succeed, but copy-list-initialization would fail. Quoting from right below the parts of [over.match.list] you quoted
In copy-list-initialization, if an
explicit
constructor is chosen, the initialization is ill-formed.
Other than that, everything else you've said is correct. The pair
constructor is an implicit conversion because the constructor is not explicit
and it's considered for overload resolution according to the second bullet of [over.match.list] because pair
doesn't have an initializer list constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With