Overload resolution works by expressing the operations required to convert the argument to the parameter type as a conversion sequence, and then ranking the conversion sequences according to some rules.
Since we are initializing references, [over.ics.ref]/2 applies:
When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the underlying type with the argument expression. Any difference in top-level cv-qualification is subsumed by the initialization itself and does not constitute a conversion.
So this allows us to use the same conversion sequence rules as if the code were:
void o_print(initializer_list<int> ol);
void o_print(vector<int> o1);
i.e. with non-const objects as parameters instead of const references.
Now:
According to [over.ics.list]/2, conversion of a braced list of int
to std::initializer_list<int>
is an identity conversion. (It wouldn't be if the list members needed promotion or conversion to int).
According to [over.ics.list]/4, conversion of a braced list to non-aggregate class (std::vector<int>
here) chooses a constructor of std::vector
by overload resolution, and this sequence is defined as a user-defined conversion sequence.
Finally we are ready to apply the ranking rules now. An identity conversion is ranked higher than a user-defined conversion sequence, so the initializer_list<int>
version wins.
NB. All references are to C++14.