(All ISO Standard references below refer to N4659: March 2017 post-Kona working draft/C++17 DIS, and all example program results are consistent over GCC and Clang for C++11, C++14 and C++17)
Consider the following example:
#include <initializer_list>
// Denote as A.
void f(float) {}
// Denote as B.
void f(std::initializer_list<int>) {}
int main() {
// Denote call as C1.
f(1.5F); // Overload resolution picks A (trivial).
// Denote call as C2.
f({1.5F}); // Overload resolution picks B (and subsequently fails; narrowing).
return 0;
}
which results in the call C2 picking overload B as best viable function, followed by a failure due to narrowing in list initialization (governed by [dcl.init.list]/7):
error: type 'float' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
f({1.5F});
function call find void f(std::initializer_list<int>)
to be the unique best viable function, ranked as a better match than void f(float)
? Afaics this conflicts with [over.ics.list]/4 and [over.ics.list]/9 (see details below).I'm looking for references to the relevant standard passages.
Note that I am aware of the special rules regarding overload resolution for list initialization (constructor overloading) and std::initializer_list<>
(and the various SO questions on this topic), where std::initializer_list<>
is strongly preferred, as governed by [over.match.list]/1. However, afaics, this does not apply here (or, if I'm wrong, at the very least arguably conflicts with the standard examples shown in [over.ics.list]/4).
For both C1 and C2 calls above, overload resolution is applied as specified in [over.call.func] (as per [over.match]/2.1 and [over.match.call]/1), particularly [over.call.func]/3, and the set of candidate functions for both calls is:
candidate_functions(C1) = { void f(float),
void f(std::initializer_list<int>) }
candidate_functions(C2) = { void f(float),
void f(std::initializer_list<int>) }
and the argument list in each call is the same as the expression-list in the call.
As per [over.match.viable]/2 and [over.match.viable]/3, the set of viable functions is the same as the candidate functions for both calls:
viable_functions(C1) = { void f(float),
void f(std::initializer_list<int>) }
viable_functions(C2) = { void f(float),
void f(std::initializer_list<int>) }
As per [over.match.best]/1, in short terms, the best viable function for a given function call, here specifically a single-argument call (with no special cases for the viable functions of each call), is the viable function that, for the single argument, has the best implicit conversion sequence from the single argument to the single parameter of the respective viable function.
The call f(1.5F)
has an identity standard conversion (no conversion) and thus an exact match for A (as per [over.ics.scs]/3) and A can be (trivially and) non-ambiguously chosen as the best viable function.
best_viable_function(C1) = void f(float)
Afaics, ranking the implicit conversion sequences of the call f({1.5F})
towards viable candidates is governed by [over.ics.list], as per [over.ics.list]/1:
When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.
Matching C2 towards A
For matching towards A where the single parameter is of the fundamental float
type, [over.ics.list]/9 and particularly [over.ics.list]/9.1 applies [emphasis mine]:
Otherwise, if the parameter type is not a class:
(9.1) if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type; [ Example:
void f(int); f( {'a'} ); // OK: same conversion as char to int f( {1.0} ); // error: narrowing
— end example ]
[...]
This arguably means that the implicit conversion sequence for matching the call f({1.5F}}
to f(float)
is the same conversion sequence as float
to float
; i.e., identity conversion and subsequently an exact match. However, as per the example above, there must be some flaw in my logic here as the call C2 doesn't even result in an ambiguous best viable function.
Matching C2 towards B
For matching towards B where the single parameter [over.ics.list]/4 applies [emphasis mine]:
Otherwise, if the parameter type is
std::initializer_list<X>
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
, or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. [ Example:void f(std::initializer_list<int>); f( {} ); // OK: f(initializer_list<int>) identity conversion f( {1,2,3} ); // OK: f(initializer_list<int>) identity conversion f( {'a','b'} ); // OK: f(initializer_list<int>) integral promotion f( {1.0} ); // error: narrowing [...]
— end example ]
In this example, the implicit conversion sequence is thus the worst conversion necessary to convert the single float
element of the list to int
, which is a conversion ranked standard conversion sequence ([over.ics.scs]/3), particularly a narrowing conversion as per [conv.fpint]/1.
Best viable function
According to my own interpretation of the standard passages as per above, the best viable function should be the same as for the C1 call,
best_viable_function(C2) = void f(float) ?
but I'm obviously missing something.
std::initializer_list
[over.ics.rank]/3.1 applies for this case, and takes precedence over the other rules of [over.ics.rank]/3 [emphasis mine]:
List-initialization sequence
L1
is a better conversion sequence than list-initialization sequenceL2
if
- (3.1.1)
L1
converts tostd::initializer_list<X>
for someX
andL2
does not, or, if not that- (3.1.2) [...]
even if one of the other rules in this paragraph would otherwise apply. [ Example:
void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // chooses #2 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo","bar"}); } // chooses #4
— end example ]
meaning that [over.ics.rank]/3.2 and [over.ics.rank]/3.3, covering distinguishing implicit conversion sequences by means of standard conversion sequences and user defined conversion sequences, respectively, does not apply, which in turn means [over.ics.list]/4 and [over.ics.list]/9 will not be used to rank the implicit conversion sequences when comparing the best match out of "C2 calls A" vs "C2 calls B".
It's not surprising that these conversions may come as counter-intuitive, and that the rules governing them are complicated. In the original C++11 and C++14 ISO standard releases, the call f({1.5F});
actually had ambiguous ranking rules w.r.t. the best viable function, which was covered in CWG Defect Report 1589 [emphasis mine]:
1589. Ambiguous ranking of list-initialization sequences
Section: 16.3.3.2 [over.ics.rank]
Status: CD4
Submitter: Johannes Schaub
Date: 2012-11-21[Moved to DR at the November, 2014 meeting.]
The interpretation of the following example is unclear in the current wording:
void f(long); void f(initializer_list<int>); int main() { f({1L});
The problem is that a list-initialization sequence can also be a standard conversion sequence, depending on the types of the elements and the type of the parameter, so more than one bullet in the list in 16.3.3.2 [over.ics.rank] paragraph 3 applies:
[...]
These bullets give opposite results for the example above, and there is implementation variance in which is selected.
[...]
Proposed resolution (June, 2014):
This issue is resolved by the resolution of issue 1467.
CWG Defect Report 1467 finally resolved also DR 1589, particularly adding the relevant part quoted from [over.ics.rank]/3 above [emphasis mine]:
1467. List-initialization of aggregate from same-type object
[...]
Proposed resolution (June, 2014):
[...]
- Move the final bullet of 16.3.3.2 [over.ics.rank] paragraph 3 to the beginning of the list and change it as follows:
[...]
even if one of the other rules in this paragraph would otherwise apply. [Example: ... — end example]
This resolution also resolves issues 1490, 1589, 1631, 1756, and 1758.
Compilers such as GCC and Clang has since back-ported DR 1467 to earlier standards (C++11 and C++14).