1

In the code below, with both versions of the print method present the first call resolves to the one with initializer_list. If I comment out the definition with initializer_list, the program seamlessly uses the vector version. In the first case I was expecting the compiler to complain!

#include <iostream>
#include <vector>
using namespace std;

void print(const vector<int>& v1){
        cout << "vector \n";
}
void print(const initializer_list<int>& il) {
         cout << "init list \n";
}

int main() {
        print({1,2,3,4,5});
        return 0;
}
101010
  • 41,839
  • 11
  • 94
  • 168

2 Answers2

1

This is how overload resolution works in C++. Both versions of print are viable for overload resolution.

  • print(const vector<int>& v1) is a viable function for overload resolution because input initializer list {1,2,3,4,5} in the caller is implicitly convertible to a std::vector<int>.
  • print(const initializer_list<int>& il) is a viable function for overload resolution cause the input type in the caller perfectly matches.

When both overloads are into play print(const initializer_list<int>& il) is chosen as the best viable function cause it's a perfect match and a perfect match has a higher priority in overload resolution than an implicit conversion.

101010
  • 41,839
  • 11
  • 94
  • 168
0

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.

M.M
  • 138,810
  • 21
  • 208
  • 365