【问题标题】:call of overloaded ‘<brace-enclosed initializer list>’ with suffices for pairs is ambiguous重载的“<brace-enclosed initializer list>”的调用足以满足对的要求是模棱两可的
【发布时间】:2020-02-05 12:44:41
【问题描述】:

我无法编译以下代码。

void print_number(long n) {
   std::cout << n << std::endl;
}

void print_number(float n) {
   std::cout << n << std::endl;
}

void print_pair(std::pair<std::string, long> p) {
   std::cout << std::get<1>(p) << std::endl;
}

void print_pair(std::pair<std::string, float> p) {
   std::cout << std::get<1>(p) << std::endl;
}

int main() {
   print_number(12l);
   print_number(3.4f);

   print_pair({"long", 12l});
   print_pair({"float", 3.4f});

   return 0;
}

print_number 函数运行良好。但是,编译器抱怨 print_pair 函数重载: error: call of overloaded ‘print_pair(&lt;brace-enclosed initializer list&gt;)’ is ambiguous.

&lt;brace-enclosed initializer list&gt;std::pair 中的内容是否无法正常工作?如何重载接收std::pair 参数的函数?

【问题讨论】:

  • 您可以通过提供显式构造函数来告诉编译器使用什么重载。
  • @OutOfBound 我该怎么办?可以举个例子吗?

标签: c++ c++11 overloading c++17


【解决方案1】:

Tl;博士

为了解决歧义,可以给编译器提供正确的类型:

print_pair(std::make_pair<std::string, long>("long", 12l));
print_pair(std::make_pair<std::string, float>("float", 3.4f));

print_pair(std::make_pair(std::string("long"), 12l));
print_pair(std::make_pair(std::string("float"), 3.4f));

歧义问题源于"long""float"不是std::string,而是const char[]这一事实。

因此,当您尝试使用以下表达式构造std::pair 时:std::pair{"long", 12l},您得到的是std::pair&lt;const char[5], long&gt;

(浮点数相同;即std::pair&lt;const char[5], float&gt;)。

您的过载print_pair 接受std::pair&lt;std::string, long&gt;std::pair&lt;std::string, float&gt;。以前的类型都不匹配,因此编译器必须执行转换。因此,它无法自动扣除您要执行的转换。两者都有效。

例如:

                    std::pair<const char[5], long>  
                                  |
  ----------------------------------------------------------------
  v                                                              v
std::pair<std::string, long>                          std::pair<std::string, float>

要“证明”问题是std::string(既不是long 也不是float),您还可以构造一个正确的std::string(而不是char 的数组)来解决歧义:

print_pair(std::make_pair(std::string("long"), 12l));
print_pair(std::make_pair(std::string("float"), 3.4f));

C++17开始,这个样板可以简化为:

using namespace std::string_literals;

print_pair(std::pair{"long"s, 12l});
print_pair(std::pair{"float"s, 3.4f});

【讨论】:

  • CTAD 是在 C++17 中引入的,而不是 C++14。
  • @Stu 刚刚注意到您的代码使用了std::tuple。 OP 是 std::pair 作为我的答案
  • @Biagio Festa 哎呀。那将是我乱七八糟的事情,看看我是否可以让它工作......是的,一旦恢复到std::pair,你的版本就可以工作了。猜猜我正在接受“一对是具有两个元素的 std::tuple 的特定情况。”字面意思...
  • @GyuHyeonChoi 您可能想要启用 C++17。使用 gcc 你必须添加 -std=c++17 选项
  • “歧义问题源于“long”和“float”不是std::string,而是const char[]这一事实。”呃,没有。如果你把它们变成真正的字符串,你会发现这仍然是模棱两可的。
【解决方案2】:

对于print_pair({"long", 12l});,有两种可能的重载:

  • void print_pair(std::pair&lt;std::string, long&gt;)
  • void print_pair(std::pair&lt;std::string, float&gt;)

{"long", 12l} 没有类型,但对于std::pair&lt;std::string, long&gt;std::pair&lt;std::string, long&gt; 都是有效的初始化。

所以调用是模棱两可的。

print_pair(std::pair{"long", 12l}); 也会有歧义
因为std::pair&lt;const char*, long&gt; 可以同样转换为std::pair&lt;std::string, long&gt;std::pair&lt;std::string, long&gt;

你必须调用它:

  • print_pair(std::pair{std::string("long"), 12l}); 进行完全匹配。

【讨论】:

  • 我可以稍后检查pair的第二个元素的类型吗?
  • 不确定你的意思.. template &lt;typename T1, typename T2&gt; void print_pair(const std::pair&lt;T1, T2&gt;&amp; p) { std::cout &lt;&lt; std::get&lt;1&gt;(p) &lt;&lt; std::endl; /*or print_number(std::get&lt;1&gt;(p))*/ } ?
  • 我应该怎样调用像print_pair({"long", 12l});这样的函数?...还有可能吗?
  • 如果有重载(或模板),我看不到用print_pair({"long", 12l});调用它的方法
【解决方案3】:

C++ 的重载解析逻辑不考虑分段转换,它只考虑给定参数/参数对的整体转换。

有了这个电话:

print_pair({"long", 12l});

我们有两个可行的候选人。一名候选人接受std::pair&lt;std::string, long&gt;,另一名候选人接受std::pair&lt;std::string, float&gt;

C++ 没有做的(以及你可能认为它做的)是分别考虑这些部分 - 并且说,"long"std::string 在两种情况下都是相同的,但 12l 到 @987654327 @ 优于 12lfloat,因此转换为 pair&lt;string, long&gt; 获胜。这如果我们有两个接受两个参数的函数,而不是两个接受pairs的函数,它会如何工作:

print_two("long", 12l); // calls print_two(string, long)

但是 C++ 不是这样工作的。它所做的就是将这些部分放在一起考虑。 braced-init-list 可用于构造 std::pair 两个特化。两者都是可行的。两者都是用户定义的转换。两者都不比另一个好,因此模棱两可。我们确实没有语言中的机制来使这种分段转换工作。

你要么直接说出你的意思:

print_pair(std::pair<std::string, long>("long", 12l));

或者取两个参数。

【讨论】:

    【解决方案4】:

    感谢所有回答。我对所有这些都投了赞成票,但是,我想做的是用异构&lt;brace-enclosed initializer list&gt;s 调用函数print_pair

    我最后做的是:

    struct number {
        number(long n) {
            std::cout << n << std::endl;
        }
        number(float n) {
            std::cout << n << std::endl;
        }
    };
    
    void print_pair(std::pair<std::string, number> p) {}
    

    这样,下面的函数调用就可以不用error: call of overloaded ‘print_pair(&lt;brace-enclosed initializer list&gt;)’ is ambiguous编译了。

    print_pair({"long", 12l});
    print_pair({"float", 3.4f});
    

    请注意,使用两个structs,其构造函数分别采用longfloat,我们将回到同样的歧义问题。

    【讨论】:

      猜你喜欢
      • 2013-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 2016-05-15
      • 1970-01-01
      相关资源
      最近更新 更多