The code is ill-formed. §8.5.4/(3.6) applies:
Otherwise, if T
is a class type, constructors are considered. The
applicable constructors are enumerated and the best one is chosen
through overload resolution (13.3, 13.3.1.7).
Now, §13.3.3.1.5 goes
When an argument is an initializer list (8.5.4), it is not an expression and special rules apply for converting
it to a parameter type. […]
if the parameter type is std::initializer_list<X>
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
, or if the initializer list has no
elements, the identity conversion.
Converting 1.1
, which is of type double
(!), to int
is a Floating-integral conversion with Conversion rank, while the conversion from 1.1
to float
is a Floating point conversion - also having Conversion rank.

Thus both conversions are equally good, and since §13.3.3.2/(3.1) cannot distinguish them either, the call is ambiguous. Note that narrowing doesn't play a role until after overload resolution is done and hence cannot affect the candidate set or the selection process. More precisely, a candidate must meet the requirement set in 13.3.2/3:
Second, for F
to be a viable function, there shall exist for each
argument an implicit conversion sequence (13.3.3.1) that converts
that argument to the corresponding parameter of F
.
However, as shown in the second quote, the implicit conversion sequence that converts {1.1}
to std::initializer_list<int>
is the worst conversion from 1.1
to int
, which is a Floating-integral conversion - and a valid (and existing!) one at that.
If instead you pass
{1.1f}
or alter the
initializer_list<float>
to
<double>
, the code is well-formed, as converting
1.1f
to
float
is an identity conversion. The standard gives a corresponding example in (3.6):
[ Example:
struct S {
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
};
S s1 = { 1.0, 2.0, 3.0 }; // invoke #1
— end example ]
Even more interestingly,
struct S {
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
};
S s1 = { 1.f }; // invoke #1
Is also valid - because the conversion from 1.f
to double
is a Floating point promotion, having Promotion rank, which is better than Conversion rank.