超负荷的呼唤是暧昧的

时间:2017-03-12 21:26:23

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

我正在学习新的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 个答案:

答案 0 :(得分:6)

当你有;

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::string,只有#2是可行的。对于右值std::string,两者都是可行的,但#3是首选(根据一般指导原则,它更具体 - 特别是由于[over.ics.rank]/3.2.3)。

答案 1 :(得分:-2)

为了清楚起见,忘掉右值参考(字符串&amp;&amp;),并假装你是编译器。

定义了两个功能:

  1. foo(std :: string str)
  2. foo(const std :: string&amp; str)
  3. 鉴于str是std :: string,您需要调用:

    foo(str);
    

    你要拨打哪个功能,1或2?

    1. 您可以将str传递给foo 1。
    2. 您可以将const引用传递给str(如指针)到foo 2。
    3. 在这种情况下你可以做任何一个,所以编译器无法决定。

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

答案 2 :(得分:-2)

模糊性介于这两个功能之间:

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

您阅读函数名称的方式是从右到左。这些函数的英语等价物是:

  1. foo是一个接受std::string参数并返回std::string的函数。
  2. foo是一个将reference转换为constant std::string参数并返回std::string的函数。
  3. 分别

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

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