为什么对模板的调用没有歧义?

Why isn't this call to a template ambiguous?

我声明了两个模板,第一个将参数 x 从类型 T 转换为类型 U,第二个将类型 U 转换为类型 T.如果我用 10 调用 cast,编译器不会报错。我认为两者都符合使用要求,因此应该有歧义,是这样吗?此代码打印 10.

#include <iostream>

template<typename T, typename U>
U cast(T x) {
    return static_cast<U>(x);
}

template<typename T, typename U>
T cast(U x) {
    return static_cast<T>(x);
}

int main() {
    std::cout << cast<int,float>(10) << '\n';
}

实例化没有歧义,因为函数参数与第一个模板参数完全匹配:文字 10 是一个 int,这也是为第一个模板类型明确指定的。

当参数类型与明确指定的类型不匹配时,您可以使实例化不明确(因此,需要进行转换),例如

// error: call of overloaded 'cast<int, float>(unsigned int)' is ambiguous
std::cout << cast<int,float>(10u) << "\n";

当您使用 cast<int, float> 时,两个模板都会被考虑。

template<typename T=int,typename U=float>
U cast(T x);
template<typename T=int,typename U=float>
T cast(U x);

然后我们替换:

template<typename T=int,typename U=float>
float cast(int x);
template<typename T=int,typename U=float>
int cast(float x);

此时,没有类型可以推导。所以我们去解决重载问题。

在一种情况下,我们可以采用 int 并在 调用 转换时转换为 float,而在另一种情况下,我们采用 int 并在 调用 转换时转换为 int。请注意,我根本没有关注演员表的 body;正文与重载决议无关。

第二个非转换(在调用点)是更好的匹配,因此选择了重载。

如果你这样做了:

std::cout << cast<int>(10) << "\n";

事情变得更有趣了:

template<typename T=int,typename U=?>
U cast(T x);
template<typename T=int,typename U=?>
T cast(U x);

对于第一个,我们无法推导出U。对于第二个,我们可以。

template<typename T=int,typename U=?>
U cast(int x);
template<typename T=int,typename U=int>
int cast(int x);

所以这里我们有一个可行的重载,它被使用了。