为什么编译器不能决定在没有引用运算符的情况下调用哪个函数?
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
时,编译器有两个选项使用您的 max
或 std::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);
5
和 6
表示临时对象,因此,它们不能作为对此类对象的非常量引用接收。
现在,在您从原型中删除 &
之后,事情会同时变好变坏。更好,因为现在你可以做到 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都是引用绑定,引用所引用的类型(int
和const 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)
没有为你编译。
下面你可以看到名为 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
时,编译器有两个选项使用您的 max
或 std::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);
5
和 6
表示临时对象,因此,它们不能作为对此类对象的非常量引用接收。
现在,在您从原型中删除 &
之后,事情会同时变好变坏。更好,因为现在你可以做到 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都是引用绑定,引用所引用的类型(int
和const 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)
没有为你编译。