1

Consider this program-

#include <string>
#include <vector>
#include <set>
void fun(const std::string& val) {
}

void fun(std::vector<std::string> val) {
}

int main()
{
    std::set<std::string> example;
    fun({std::begin(example), std::end(example)});
}

On compilation, I am hitting into these errors-

prog.cc: In function 'int main()':
prog.cc:13:49: error: call of overloaded 'fun(<brace-enclosed initializer list>)' is ambiguous
   13 |     fun({std::begin(example), std::end(example)});
      |                                                 ^
prog.cc:4:6: note: candidate: 'void fun(const string&)'
    4 | void fun(const std::string& val) {
      |      ^~~
prog.cc:7:6: note: candidate: 'void fun(std::vector<std::__cxx11::basic_string<char> >)'
    7 | void fun(std::vector<std::string> val) {
      |      ^~~

I understand that std::string has a constructor overload that takes in an initializer_list like so-

basic_string( std::initializer_list<char> ilist,
              const Allocator& alloc = Allocator() );

and std::vector<std::string> has an overload that looks like so-

vector( std::initializer_list<std::string> init,
        const Allocator& alloc = Allocator() );

So, it is clear that these 2 methods differ in their types. One takes in an initializer_list of char and the other of type std::string.

In my code when I am passing an initializer list of strings cos I pass 2 iterators to a set of strings.

Even then, why does the compiler flag this as an ambiguous call?

Pavan Manjunath
  • 27,404
  • 12
  • 99
  • 125
  • 1
    "_In my code when I am passing an initializer list of strings cos I pass 2 iterators to a set of strings."_ No, your initializer list has two iterators; it is not a list of strings. – Asteroids With Wings Mar 01 '20 at 21:43
  • 1
    Are you aware that you don't _have_ an initializer list of `std::string`, but instead of `std::set::const_iterator`? – Mooing Duck Mar 01 '20 at 21:44
  • 1
    Furthermore it's not actually an `std::initializer_list` unless & until that's actually what the initializer expression binds to. e.g. any `void fun(T)` will work if `T` had a `T(std::set::const_iterator, std::set::const_iterator`). Or something that worked equivalently given some conversion chain.... – Asteroids With Wings Mar 01 '20 at 21:44
  • @MooingDuck Where's the `const_` from? – Asteroids With Wings Mar 01 '20 at 21:46

1 Answers1

5

The compiler sees an ambiguous call to the following two constructors (note that neither of them take an initializer list):

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

and

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

Now, if you were to actually call the std::string constructor with those iterator arguments you'd get an error, because they don't dereference to a char. But since that check is not part of the function declaration (e.g. via SFINAE), you're getting the ambiguity error instead.

AVH
  • 11,349
  • 4
  • 34
  • 43
  • Interesting. But the compilation error( the part which says `call of overloaded 'fun()'`) seems to indicate that the compiler has deduced it as an `initializer list`. I'm guessing this initializer list is not the same as `std::initializer_list>`? – Pavan Manjunath Mar 01 '20 at 22:03
  • Are you using Microsoft's compiler? I've seen that as a goto message when the compiler is confused. – lakeweb Mar 01 '20 at 22:06
  • Also, the compiler error indicates that `{begin(), end()}` is being considered as one parameter. Why is it trying to match with constructors that take at least 2 parameters? – Pavan Manjunath Mar 01 '20 at 22:09
  • @lakeweb No, GCC 10 and C++14 – Pavan Manjunath Mar 01 '20 at 22:10
  • 3
    @PavanManjunath That's because it is a literal "initializer list" to initialize something (which may or may not be a `std::initializer_list`, for example, here it is either a vector or string), which hasn't been resolved to a type yet, so that's all the compiler can say. Like `std::vector{5, "abc"}` doesn't call the `std::initializer_list` constructor, but it is still initialized with an brace-enclosed-init-list (With 2 arguments). – Artyer Mar 01 '20 at 22:12
  • As an aside I guess this sort of scenario is why more recent library additions have phrases like "this function does not participate in overload resolution unless..." scattered throughout them, an allusion to `std::enable_if` which may make spurious errors less common but also makes reference material much harder to parse! – Asteroids With Wings Mar 01 '20 at 22:12
  • @PavanManjunath "brace-enclosed initializer list" and `std::initializer_list` are not the same thing. – Asteroids With Wings Mar 01 '20 at 22:12
  • @PavanManjunath _"Also, the compiler error indicates that {begin(), end()} is being considered as one parameter."_ No, it isn't. You're misintepreting it. – Asteroids With Wings Mar 01 '20 at 22:13
  • @ Artyer, thanks for your comment. I will pay more attention the next time I see that error. – lakeweb Mar 01 '20 at 22:18
  • @Artyer Thanks for the nice example. One last qn- in my case the brace-enclosed-list has 2 elements, both of them being std::set iterators. In such case, shouldn't it have binded to the vector constructor that takes in a std::initializer_list? since `std::initializer_list<>` ctors are preferred over other ctors?(https://stackoverflow.com/a/18224556/457237) – Pavan Manjunath Mar 02 '20 at 00:29
  • @PavanManjunath The constructor you're talking about has the following signature: `vector (std::initializer_list init, const Allocator& alloc = Allocator());`. In this case the types in what would/could be an `std::initializer_list` are clearly not `T`, but `std::set::const_iterator`. – AVH Mar 02 '20 at 08:49