超载的调用是模棱两可的

Call of overload is ambigious

本文关键字:模棱两可 调用 超载      更新时间:2023-10-16

我正在学习新的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);

为什么通话模棱两可?

当你有;

std::string res = foo(str);

有两个可行的候选者:

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

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

通常,偏爱一个函数而不是另一个函数的规则与哪个函数更具体有关。例如,给定一个函数取string&和一个取string const&的函数,前者只能用对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)。

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

定义了两个函数:

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

给定 str 是一个 std::字符串,要求您调用:

foo(str);

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

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

在这种情况下,您可以执行任一操作,因此编译器无法决定。

编译器如何决定?有一些规则涉及创建候选函数列表、可以对参数执行哪种类型提升或转换,以及可以使用哪些构造函数来创建所需的参数。这是一门课程的简单概述。

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

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

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

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

分别。

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

通常,如果它不是基元类型(即 int、char、short 等),请使用引用而不是类型本身。对于所有意图和目的,std::string类似于std::vector<char>因此,无论字符串的长度如何,通过引用传递它始终会花费您sizeof(pointer)数据事务。