为什么编译器不能决定在没有引用运算符的情况下调用哪个函数?

Why compiler cannot decide which function to call without the reference operator?

下面你可以看到名为 max 的小函数。

template <typename T>
T max(T& arg1, T& arg2) {    
    if (arg1 > arg2) {
        return arg1;
    } else {
        return arg2;
    }
}

当我在 main 中调用这个函数时,它完美地工作,但是如果我从参数中删除引用

T max(T arg1, T arg2)  

然后编译器给出如下错误

main.cpp:18:21: error: call of overloaded 'max(int&, int&)' is ambiguous

很明显,编译器无法决定是调用我的函数还是标准函数。这里的问题是,当论证头上有参考时,它如何决定并正常工作?

当我用常量参数调用函数时

const int a = 12;
const int b = 24;
cout << max(a, b) << endl;

它调用标准的 max 函数。但是当我调用非常量值时,它会调用我的函数。

我可以理解,如果对象是const,const 函数将在别处被调用,非const 函数将被调用。但是为什么要参考触发这个机制呢?为什么没有引用运算符就不能决定?

当你使用

const int a = 12;
const int b = 24;
cout << max(a, b) << endl;

编译器别无选择,只能调用标准 max 函数,因为它被定义为接收常量引用(我几乎可以肯定你的 CPP 文件开头有 using namespace std;) .

当您使用非 const 参数调用 max 时,编译器有两个选项使用您的 maxstd::max。在这种情况下,编译器总是选择限制较少的选项,即 T max(T& arg1, T& arg2).

因为 const 输入参数被视为函数签名的一部分

T max(T& arg1, T& arg2)

T max(const T& arg1, const T& arg2)

不一样,不会造成歧义错误,而

T max(T arg1, T arg2)

会导致第一种情况的歧义错误。

Max是标准库中已经定义的函数,其原型如下:

template< class T > 
const T& max( const T& a, const T& b );

如果您有一个文件,其中包含算法:

#include <algorithm>

而且你使用:

using namespace std;

那你的麻烦来了,因为你的电话有歧义

的版本上运行良好
T max(T& arg1, T& arg2)

因为你缺少constnesses,并且编译器可以识别调用哪个函数,当你的函数有值时,编译器应该选择哪个函数是不明确的,所以它给你模棱两可的错误。

您的函数的先前版本不正确。您不应将 max() 的参数声明为 T&(非常量),因为在这种情况下,您将无法执行此操作:

int m = max(5, 6);

56 表示临时对象,因此,它们不能作为对此类对象的非常量引用接收。

现在,在您从原型中删除 & 之后,事情会同时变好变坏。更好,因为现在你可以做到 max(5, 6)。更糟糕的是,因为全局命名空间中还有另一个函数,它也可以完成这项工作 - "standard" max(const T&, const T&).

问题来了 - 应该使用哪一个?如果将参数作为常量传递,两种选择都同样好——编译器可以创建临时对象并通过 const& 传递它们(如果它选择标准 max())或按值传递它们,这也意味着调用适当的构造函数.这就是为什么你会收到关于 "ambiguous call".

的错误

解决方案:

  • 使用std::max()(为什么不呢?)
  • 不要将 std::max() 导入全局命名空间(使用 using namespace std or using std::max

恕我直言,第一个选项更可取。

这是一个关于重载解析的问题,目前还没有人解决。让我们暂时忽略模板,因为它们不是严格相关的,让我们假装我们声明了这些重载:

void foo(int& );       // (1)
void foo(const int&);  // (2)

当我们尝试用左值调用 foo 时会发生什么?

int i;
foo(i);

两位候选人都是可行的。确定哪个是 最佳 可行候选者的第一个规则是转换序列,并且 [over.ics.rank]:

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
— [...]
— S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.

S1和S2都是引用绑定,引用所引用的类型(intconst int)除了cv-qualifiers之外都是一样的,但是S2的引用类型更多的是cv - 比 S1 合格。最不合格的参考获胜。因此, (1) 是首选。

这正是您的函数模板

的原因
template <typename T> T max(T&, T&); // (1)

优于标准函数模板:

template <typename T> T const& max(T const&, T const&); // (2)

问题的第二部分介绍了这个额外的重载:

void foo(int& );       // (1)
void foo(const int&);  // (2)
void foo(int );        // (3)

我们知道 (1) 优于 (2),但是 不存在 区分 (1) 和 (3) 的规则。没有 "reference is better than non-reference" 或反之亦然的规则。因此,两位候选人都同样可行——这是病式的。因此,关于歧义的错误。这就是为什么:

template <typename T> T max(T, T); // (3)
template <Typename T> T const& max(T const&, T const&); // (2)

没有为你编译。