调用重载<brace-enclosed初始化列表>是不明确的,如何处理?

rsk*_*k82 19 c++ initializer-list ambiguous c++11

我真的不明白这一点,我认为编译器首先执行大括号中的内容然后将结果赋予最合适的函数.这看起来它给函数一个初始化列表来处理它...

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

void func(vector<string> v) { }

void func(vector<wstring> v) { }

int main() {
  func({"apple", "banana"});
}
Run Code Online (Sandbox Code Playgroud)

错误:

<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> >)
Run Code Online (Sandbox Code Playgroud)

为什么不func(vector<string> v)调用我的超载,我可以这样做吗?

And*_*owl 24

这个很微妙.

std::vector有一个构造函数采用两个范围迭代器.它是一个模板构造函数(在C++ 11标准的23.6.6.2中定义):

template<typename InputIterator>
vector(InputIterator first, InputIterator last, 
const allocator_type& a = allocator_type());
Run Code Online (Sandbox Code Playgroud)

现在,std::vector<wstring>接受initializer_lista 的构造函数const char*与函数调用中的隐式转换不匹配(并且string是不同的类型); 但是上面的那个,当然包括in std::vector<string>和in std::vector<wstring>,是一个潜在的完美匹配,因为InputIterator可以推断出来const char*.除非使用某些SFINAE技术来检查推导出的模板参数是否确实满足InputIterator向量的基础类型的概念(这不是我们的情况),否则此构造函数是可行的.

但话又说回来,无论是std::vector<string>std::vector<wstring>有一个可行的构造函数,实现从支撑初始化列表转换:因此,在不确定性.

所以问题在于,虽然"apple"并且"banana"不是真正的迭代器(*),但它们最终被视为这样.添加一个参数"joe"的函数调用修复由非模糊呼叫的问题,因为它强制编译器排除基于范围的构造函数和选择只有可行的转换(initializer_list<wstring>不是可行的,因为const char*不能被转换成wstring).


*实际上,它们是指针const char,因此它们甚至可以被视为字符的常量迭代器,但绝对不是字符串,因为我们的模板构造函数愿意思考.

  • 现在我更讨厌所谓的"统一初始化",它干扰了我喜欢的功能,`std :: initializer_list`. (2认同)
  • @AndyProwl因为对于`vector <wstring>`,它选择模板构造函数然后两个`func'匹配,没有人比另一个好.如果其中一个`func`的参数类型为`std :: initializer_list <X>`而另一个不具有,那么`func`将是首选.它与嵌套的重载决策上下文无关,函数的一个参数使用初始化列表构造函数来初始化自身.重要的是参数类型本身,在两种情况下都是`vector <X>`. (2认同)