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?

+3


source to share


1 answer


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 take std;:initializer_list

s. Strong. If there is a c constructor for compilers in any way to interpret a call using a simplified initializer std::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 class T

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

.

+3


source







All Articles