【问题标题】:Call of overload is ambigious重载的调用不明确
【发布时间】:2017-03-12 21:26:22
【问题描述】:

我正在学习新的 C++ 语义,但这个程序出现错误:

#include <iostream>
#include <string>
#include <utility>

std::string foo(std::string str)
{
    return str + " call from normal";
}

std::string foo(const std::string& str)
{
    return str + " call from normal";
}

std::string foo(std::string&& str)
{
     return str + " call from ref ref";
}

int main()
{
    std::string str = "Hello World!";
    std::string res = foo(str);
    std::string&& res_ref = foo(std::move(str));
    std::cout << "Res ref = " << res_ref << std::endl;
    std::cout << "Str = " << str << std::endl;
    return 0;
}

错误是:

:23:30: error: call of overloaded ‘foo(std::__cxx11::string&)’ is ambiguous
    std::string res = foo(str);

为什么调用不明确?

【问题讨论】:

  • 你有 3 个foo 函数,它无法确定你要使用哪一个。
  • 但是它的双重引用,标准函数和常量引用,为什么会出现这个问题?当我评论第一个函数声明时一切正常,我的问题是:为什么编译器不能选择一个用途?
  • 编译器应该如何知道是调用std::string foo(std::string str) 还是std::string foo(const std::string&amp; str)?您以完全相同的方式调用它们
  • 好的...那么有没有办法强制编译器使用标准方法?
  • 不,编译器无法区分这两个函数——而且我想不出这在任何情况下都有用。

标签: c++ c++14 overload-resolution


【解决方案1】:

当你有的时候;

std::string res = foo(str);

有两个可行的候选人:

foo(std::string );         // #1
foo(std::string const& );  // #2

当给定多个候选函数时,确定选择哪个函数有很多很多步骤。但是在这种情况下,这两种选择是完全无法区分的——在stringstring const&amp; 之间的重载决议中根本没有偏好。同样,stringstring&amp;&amp; 之间对于右值参数没有偏好,因此您的第二次调用也被认为是模棱两可的。

一般来说,优先选择一个功能而不是另一个功能的规则与哪个功能更具体有关。例如,给定一个采用string&amp; 的函数和一个采用string const&amp; 的函数,前者只能用对string 的非常量左值引用来调用,但后者可以用一大堆东西来调用,所以当两者是可行的,前者是首选(特别是由于[over.ics.rank]/3.2.6)。但在这种情况下,任何你可以调用 #1 的东西,你都可以调用 #2 。任何你可以打电话给#2,你都可以打电话给#1。因此,没有任何理由偏爱其中之一。

你应该简单地移除那个重载,留下你的两个:

foo(std::string const& ); // #2
foo(std::string&& );      // #3

对于左值std::strings,只有#2 是可行的。对于右值std::strings,两者都是可行的,但首选#3(根据一般准则,它更具体 - 特别是由于[over.ics.rank]/3.2.3)。

【讨论】:

  • 用引用备份你的断言。
  • @NickWestgate 这不是 Skeptics.SE。是否有任何声明特别您质疑其有效性?
  • @NickWestgate :我很欣赏这个讽刺。 ;-]
  • @Barry:不,不,你的答案读起来像教科书(我试图避免),但我对你的观点没有异议。不过,您还没有解释为什么 #1 和 #2 无法区分。无论如何,学习者(似乎不是英语母语)对您的回答更满意。真的不需要与您的代表一起踩其他答案...
【解决方案2】:

为了清楚起见,忘记右值引用 (string&&) 之一,只是假装你是编译器。

定义了两个函数:

  1. foo(std::string str)
  2. foo(const std::string& str)

鉴于 str 是一个 std::string 你被要求调用:

foo(str);

你要调用哪个函数,1 还是 2?

  1. 您可以将 str 传递给 foo 1。
  2. 您可以将 str 的 const 引用(如指针)传递给 foo 2。

在这种情况下你可以做任何一个,所以编译器无法决定。

编译器如何决定?关于创建候选函数列表、可以对参数执行哪种类型的提升或转换以及可以使用哪些构造函数来创建所需的参数,都有一些规则。 Here is a simple overview 来自课程。

【讨论】:

  • “你可以做任何一个”只是意味着两者都是可行的 - 说它是模棱两可是不够的。如果 #1 也是 foo(std::string&amp;),您也可以这样做,但这种情况不会模棱两可。
  • 感谢您对您的反对意见发表评论。我只是想为学习者保持简单,并提供了指向更详细答案的链接。
  • 问题是你把它过分简化了——你的回答说,任何时候你有两个可行的候选人,电话都是模棱两可的。这不是真的。
【解决方案3】:

这两个函数之间存在歧义:

std::string foo(std::string str); // 1
std::string foo(const std::string& str); // 2

读取函数名的方式是从右到左。这些函数的英文等价物是:

  1. foo 是一个接受 std::string 参数并返回 std::string 的函数。
  2. foo 是一个函数,它接受 referenceconstant std::string 参数并返回 std::string

分别。

编译器知道这两个函数签名之间的唯一区别是是将str 的副本作为参数还是对str 的不可变引用。从编译器的角度来看,这两个函数没有足够的差异,因此在运行时对它们有偏好。

一般来说,如果它不是原始类型(即 int、char、short 等),请使用引用而不是类型本身。出于所有意图和目的,std::stringstd::vector&lt;char&gt; 相似,因此无论字符串的长度是多少,通过引用传递它总是会花费您sizeof(pointer) 数据事务。

【讨论】:

  • 谁能告诉我我错在哪里(因为投反对票),这样我也可以从中学习:)
猜你喜欢
  • 1970-01-01
  • 2011-06-08
  • 2014-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多