call of overloaded is ambiguous, how to de

2019-06-14 22:47发布

I really don't understand this, I thought that compiler first executes what is in braces and then gives the result to the most appropriate function. Here it looks like it gives the function an initializer list to deal with it...

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

void func(vector<string> v) { }

void func(vector<wstring> v) { }

int main() {
  func({"apple", "banana"});
}

error:

<stdin>: In function 'int main()':
<stdin>:11:27: error: call of overloaded 'func(<brace-enclosed initializer list>)' is ambiguous
<stdin>:11:27: note: candidates are:
<stdin>:6:6: note: void func(std::vector<std::basic_string<char> >)
<stdin>:8:6: note: void func(std::vector<std::basic_string<wchar_t> >)

Why isn't my func(vector<string> v) overload called, and can I make it so?

1条回答
Emotional °昔
2楼-- · 2019-06-14 23:16

This one was subtle.

std::vector has a constructor taking two range iterators. It is a template constructor (defined in 23.6.6.2 of the C++11 Standard):

template<typename InputIterator>
vector(InputIterator first, InputIterator last, 
const allocator_type& a = allocator_type());

Now the constuctor of std::vector<wstring> accepting an initializer_list is not a match for the implicit conversion in your function call, (const char* and string are different types); but the one above, which is of course included both in std::vector<string> and in std::vector<wstring>, is a potentially perfect match, because InputIterator can be deduced to be const char*. Unless some SFINAE technique is used to check whether the deduced template argument does indeed satisfy the InputIterator concept for the vector's underlying type, which is not our case, this constructor is viable.

But then again, both std::vector<string> and std::vector<wstring> have a viable constructor which realizes the conversion from the braced initializer list: hence, the ambiguity.

So the problem is in the fact that although "apple" and "banana" are not really iterators(*), they end up being seen as such. Adding one argument "joe" to the function call fixes the problem by disambiguating the call, because that forces the compiler to rule out the range-based constructors and choose the only viable conversion (initializer_list<wstring> is not viable because const char* cannot be converted to wstring).


*Actually, they are pointers to const char, so they could even be seen as constant iterators for characters, but definitely not for strings, as our template constructor is willing to think.

查看更多
登录 后发表回答