21

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

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

void func(vector<string> v) { }

void func(vector<wstring> v) { }

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

错误:

<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> >)

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

4

1 回答 1

25

这个很微妙。

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

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

std::vector<wstring>现在接受 an的构造函数与initializer_list函数调用中的隐式转换不匹配,(const char*并且string是不同的类型);但是上面的,当然包含在std::vector<string>和 中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,因此它们甚至可以被视为字符的常量迭代器,但绝对不是字符串的,正如我们的模板构造函数所愿意考虑的那样。

于 2013-01-29T17:34:09.233 回答