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

时间:2015-11-10 14:07:54

标签: c++ templates overload-resolution

下面你可以看到名为max。

的小功能
unarchiveObjectWithData

当我在main中调用此函数时,它完全有效,但是如果我从参数中删除引用

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

然后编译器给出以下错误

T max(T arg1, T arg2)  

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

当我用恒定参数调用函数时

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

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

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

5 个答案:

答案 0 :(得分:6)

这是一个关于重载决议的问题,但尚无人解决。我们暂时忽略模板,因为它们并不严格相关,让我们假装我们宣布这些重载:

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

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

int i;
foo(i);

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

  

标准转换序列S1是比标准转换序列更好的转换序列   S2如果
   - [...]
   - S1和S2是引用绑定(8.5.3),引用引用的类型相同   除了顶级cv限定符之外的类型,以及由S2初始化的引用所引用的类型   比由S1初始化的引用所引用的类型更符合cv。

S1和S2都是引用绑定,引用引用的类型(intconst int)除了cv-qualifiers外是相同的,但S2的引用类型是比S1更具有cv资格。最不合格的参考获胜。因此,(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)的规则。没有&#34;参考优于非参考&#34;反之亦然。因此,两个候选人都同样可行 - 这是不正确的。因此关于歧义的错误。这就是原因:

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

没有为你编译。

答案 1 :(得分:1)

使用时

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

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

当您使用非max参数调用const时,编译器有两个选项可以使用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)

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

答案 2 :(得分:1)

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

int m = max(5, 6);

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

现在,从原型中删除&之后,事情变得越来越糟。更好,因为现在你可以做max(5, 6)。更糟糕的是,因为全局命名空间中还有另一个函数,它也可以完成这项工作 - “标准”max(const T&, const T&)

这就是问题 - 应该使用哪一个?如果将参数作为常量传递,则两个选项都同样好 - 编译器可以创建临时对象并通过const&传递它们(如果它选择标准max())或者通过值传递它们,这也意味着调用正确的构造函数。这就是为什么你得到关于“模棱两可的电话”的错误。

解决方案:

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

恕我直言第一选择更为可取。

答案 3 :(得分:-1)

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,并且编译器可以识别要调用哪个函数,当你的函数有值时,那么函数编译器应该选择它是不明确的,所以它会给你一个模糊的错误。

答案 4 :(得分:-2)

一个好的解决方案是使用通用引用。 您的程序将使用值和变量。

您的功能将如下所示:

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

通用引用是编译器使用模板立即上下文生成一个带右值引用或左值引用的函数。

修改

阅读其他答案,我发现你的功能名称与std::max发生冲突 只是重命名功能完美无缺。