Uniform initialization behavior for different types in a vector
I came across this article in which I was reading this example from one of the posters. I have included this here for convenience.
struct Foo
{
Foo(int i) {} // #1
Foo() {}
};
int main()
{
std::vector<Foo> f {10};
std::cout << f.size() << std::endl;
}
The above code, as written, emits "1" (10 is converted to Foo by a constructor that takes an int, then vectors are the initializer_list constructor). If I comment out the line commented out as # 1, the result is "10" (the initializer_list file cannot be converted, so an int constructor).
My question is why is it emitting 10 when the int constructor is removed. I understand that the uniform initialization list works in the following order
1-Calls the initializer list if available or possible
2-Calls the default constructor if available
3-Does aggregate initialization
In the above case, why is it creating 10 elements in the vector, since 1,2 and 3 are not possible? Does this mean that with uniform initialization, a vector of elements can always have different types of behavior?
source to share
Borrowing quote from Scott Meyers in Effective Modern C ++ (emphasis in original):
If, however, one or more constructors declare a type parameter
std::initializer_list
, calls using forced-initialization syntax strongly prefer overloads that takestd;:initializer_list
s. Strong. If there is a c constructor for compilers in any way to interpret a call using a simplified initializerstd::initializer_list
, compilers will use that interpretation.
So when you have it std::vector<Foo> f {10};
, it will try to use the constructor vector<Foo>
that it takes initializer_list<Foo>
. If Foo
is constructive from int
, then there is a constructor that we use - so we get one Foo
built from 10
.
Or, from the standard, in [over.match.list]:
When objects of a non-aggregate type are
T
initialized over a list (8.5.4), overload resolution selects the constructor in two phases:(1.1) - Initially, candidate functions are initializer list constructors (8.5.4) of a class
T
and the Argument List consists of an initializer list as one argument.
(1.2). If no viable initializer list constructor is found, overload resolution is performed again, where the Candidate Functions are all the constructors of the classT
and the argument list consists of the members of the initializer list.
If a viable initializer list constructor exists, it is used. If you didn't have a constructor Foo(int )
, there wouldn't be a viable list-initializer-constructor, and in the second case, overload resolution would detect a constructor vector
that takes a size, and so you get a vector of 10 built by default Foo
.
source to share