【问题标题】:Why does the number of elements in a initializer list cause an ambiguous call error?为什么初始化列表中的元素数量会导致模棱两可的调用错误?
【发布时间】:2017-05-21 07:52:58
【问题描述】:

为什么编译器对doSomething 的前两次调用正常,但使用列表中的两个元素会导致调用不明确?

#include <vector>
#include <string>

void doSomething(const std::vector<std::string>& data) {}

void doSomething(const std::vector<int>& data) {}

int main(int argc, char *argv[])
{
    doSomething({"hello"}); // OK
    doSomething({"hello", "stack", "overflow"}); // OK
    doSomething({"hello", "stack"}); // C2668 'doSomething': ambiguous call

    return 0;
}

【问题讨论】:

    标签: c++ initializer-list ambiguous overload-resolution


    【解决方案1】:

    单参数和三参数列表都只能匹配std::vector&lt;std::string&gt;std::initializer_list 构造函数。但是,两个参数列表匹配来自std::vector&lt;int&gt; 的构造函数之一:

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

    实际上,char const * 可以递增,也可以取消引用以获得可隐式转换为 intchar

    【讨论】:

      【解决方案2】:

      这里发生的是,在两个元素初始化器列表中,两个字符串文字都可以隐式转换为const char*,因为它们的类型是const char[N]。现在std::vector 有一个构造函数,它接受两个指针符合条件的迭代器。因为std::vector&lt;std::string&gt;initializer_list构造函数与std::vector&lt;int&gt;的迭代器范围构造函数冲突。

      如果我们将代码改为

      doSomething({"hello"s, "stack"s});
      

      那么初始化列表的元素现在是std::strings,所以没有歧义。

      【讨论】:

      • doSomething(std::vector&lt;std::string&gt;({"hello", "stack"}));,但您的解决方案更具可读性。无论如何,如果这是在真实代码中,我可能还会添加一个小注释来解释看似无用的显式性。
      • 我认为如果可能的话,总是调用 initializer_list 构造函数,例如在 std::vector{5,1} 中,一个向量得到一个元素为 5 和 1 而不是 5 乘以 1 的向量。为什么这里不是这样吗?
      • @ab.o2c 首选初始化列表构造函数,但在这种情况下我们没有std::vector&lt;int&gt;{5,1}。我们拥有的是std::vector&lt;int&gt;{const char*, const char*}。由于初始化列表必须与向量的元素具有相同的类型,因此在这种情况下将被忽略并选择迭代器构造函数。
      • @NathanOliver 你是对的,我的答案是错误的。我以为你说过,两个向量的迭代器构造函数是冲突的,但这不是你的答案 :-) 我需要一些测试才能得到这一点。现在为您 +1。
      • 喜欢使用string operator " " s的解决方案
      【解决方案3】:

      "hello""stack" 都衰减为满足InputIterator 概念的const char *。这允许它们匹配std::vector's constructor #4

      如果您传递 std::string 对象,歧义就会得到解决。

      【讨论】:

        【解决方案4】:

        也许你可以用 initializer_list 参数重载 doSomething(),像这样:

        void doSomething(initializer_list<string> l) {
            doSomething(vector<string>(l));
        }
        

        然后,doSomething({"hello", "stack"}); 将调用您想要的函数。

        【讨论】:

        • “我不确定这是否有效” - 请仅在您知道它们有效的情况下提供答案。其他答案解释了问题发生的原因,因此传达了更多信息,将解决方案留给用户来确定最佳行动方案。
        • 感谢提醒,我在linux/clang++/c++11中测试过,运行良好。
        猜你喜欢
        • 2020-10-25
        • 1970-01-01
        • 1970-01-01
        • 2013-05-23
        • 2015-07-22
        • 1970-01-01
        • 2018-04-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多