我遇到了一种看起来非常意外的重载解决行为。以下代码被 gcc 和 clang 拒绝并出现歧义错误:
template <typename T>
struct A
{
typedef T key_type;
};
template <typename T>
void foo(A<T> rng, T val);
template <typename T, typename U = T>
void foo(T, typename U::key_type);
int main()
{
A<int> i;
foo(i, 0);
}
错误是:
test.cpp:16:5: error: call to 'foo' is ambiguous
foo(i, 0);
^~~
test.cpp:8:6: note: candidate function [with T = int]
void foo(A<T> rng, T val);
^
test.cpp:11:6: note: candidate function [with T = A<int>, U = A<int>]
void foo(T, typename U::key_type);
^
我希望两者都是完全匹配的,但第一个重载会在部分排序中获胜,因为在第一个参数A<T>
中比T
.
让我大吃一惊的是,如果我将第二个签名更改为:
template <typename T, typename U = T>
void foo(T, typename T::key_type);
gcc 和 clang 现在都接受代码,并按照我最初的预期选择第一个重载。
我看不出这种改变会如何改变行为:我所做的只是将既没有明确指定也没有推导的模板参数的使用替换为U
默认值(T
)。
再说一次,改变之前的行为是出乎意料的,所以也许我错过了一些东西。
有人可以解释一下:
- 为什么第一种情况不明确;和
- 为什么我所做的更改解决了歧义?
如果相关,我测试的编译器版本是 gcc 4.8.0 和最近的 clang 主干版本。