Why does the number of elements in a initializer list cause an ambiguous call error?

3k views Asked by At

Why are the first two calls to doSomething OK by the compiler, but using two elements in the list causes an ambiguous call?

#include <vector>
#include <string>

void doSomething(const std::vector<std::string>& data) {}

void doSomething(const std::vector<int>& data) {}

int main(int argc, char *argv[])
{
    doSomething({"hello"}); // OK
    doSomething({"hello", "stack", "overflow"}); // OK
    doSomething({"hello", "stack"}); // C2668 'doSomething': ambiguous call

    return 0;
}
4

There are 4 answers

6
NathanOliver On BEST ANSWER

What is happening here is that in the two element initializer list both of the string literals can be implicitly converted to const char* since their type is const char[N]. Now std::vector has a constructor that takes two iterators which the pointers qualify for. Because of that the initializer_list constructor of the std::vector<std::string> is conflicting with the iterator range constructor of std::vector<int>.

If we change the code to instead be

doSomething({"hello"s, "stack"s});

Then the elements of the initializer list are now std::strings so there is no ambiguity.

0
Quentin On

Both the one-argument and three-argument lists can only match std::vector<std::string>'s std::initializer_list constructor. However, the two-argument list matches one of the constructors from std::vector<int>:

template <class InputIt>
vector(InputIt first, InputIt last, Allocator const &alloc = Allocator());

Indeed, a char const * can be incremented, and dereferenced to get a char that is implicitly convertible to an int.

0
François Andrieux On

"hello" and "stack" both decay to const char * which satisfies the InputIterator concept. This allow's them to match std::vector's constructor #4.

If you pass std::string objects the ambiguity is resolved.

2
neolc On

may be you can overload the doSomething() with initializer_list parameter, like this:

void doSomething(initializer_list<string> l) {
    doSomething(vector<string>(l));
}

then, doSomething({"hello", "stack"}); will call the function you desired.