【问题标题】:call of overloaded <brace-enclosed initializer list> is ambiguous, how to deal with that?重载 <brace-enclosed initializer list> 的调用不明确,如何处理?
【发布时间】:2013-01-13 06:58:51
【问题描述】:

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

#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&lt;string&gt; v) 重载,我可以这样做吗?

【问题讨论】:

    标签: c++ c++11 initializer-list ambiguous


    【解决方案1】:

    这个很微妙。

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

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

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

    但话又说回来,std::vector&lt;string&gt;std::vector&lt;wstring&gt; 都有一个可行的构造函数,它实现了从花括号初始化列表的转换:因此,歧义。

    所以问题在于尽管"apple""banana" 并不是真正的迭代器(*),但它们最终被视为这样。在函数调用中添加一个参数 "joe" 通过消除调用歧义来解决问题,因为这会强制编译器排除基于范围的构造函数并选择 only 可行的转换(initializer_list&lt;wstring&gt; 是 不可行,因为const char* 无法转换为wstring)。


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

    【讨论】:

    • @OmnipotentEntity:谢谢 :-)
    • 现在我更讨厌所谓的“统一初始化”,它干扰了我喜欢的功能,std::initializer_list
    • @Praetorian:你说得对,确实如此,但作为chars 的常量迭代器,而不是strings 或wstrings 的常量迭代器。
    • @AndyProwl 你的描述让我觉得“这个构造函数比初始化列表构造函数更匹配,所以它是首选”。但是,即使如果将构造函数相互比较,模板构造函数会更好地匹配,编译器仍然会使用初始化列表构造函数。因为编译器首先only查看初始化列表构造函数。只有当它们根本不匹配时,它才会考虑所有其他构造函数,这次将初始化列表元素单独作为参数,而不是整个列表。
    • @AndyProwl 因为对于vector&lt;wstring&gt;,它选择模板构造函数,然后两个funcs 匹配,没有一个比另一个更好。如果funcs 之一具有std::initializer_list&lt;X&gt; 类型的参数,而另一个没有,则首选func。没关系,在嵌套重载决议上下文中,函数的一个参数使用初始化列表构造函数来初始化自身。重要的是参数类型本身,在这两种情况下都是vector&lt;X&gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多