在下面的代码中,我有一个非成员模板函数和一个针对 int
类型的完全特化。
#include <iostream>
template <typename U>
void f(const U& x, const U& y)
{
std::cout << "generic " << x << " " << y << std::endl;
}
template <>
void f(const int& x, const int& y)
{
std::cout << "specialization int " << x << " " << y << std::endl;
}
int main()
{
int a = 1;
f(a, a);
f('a', 'a');
f('a', 1); // Compiler error
// f<int>('a', 1); // This compiles
return 0;
}
虽然从 char
到 int
的隐式转换在该语言中可用,但编译器(g++ 7.3.0 和 clang 6.0.1)不会编译代码,报错
error: no matching function for call to ‘f(char, int)’
deduced conflicting types for parameter ‘const U’ (‘char’ and ‘int’)
虽然很清楚为什么模板推导不起作用,但我不清楚为什么编译器在丢弃通用模板后不考虑隐式转换。例如,如果我使用 U=int
显式实例化 f
取消注释代码中的相应行作为
f<int>('a', 1);
然后代码编译并正确给出输出
specialization int 1 1
generic a a
specialization int 97 1
相反,如果我用 f
的重载 来补充代码,而不是模板特化 as
#include <iostream>
template <typename U>
void f(const U& x, const U& y)
{
std::cout << "generic " << x << " " << y << std::endl;
}
void f(const int& x, const int& y)
{
std::cout << "overload int " << x << " " << y << std::endl;
}
int main()
{
int a = 1;
f(a, a);
f('a', 'a');
f('a', 1);
return 0;
}
然后代码编译并给出预期的输出
overload int 1 1
generic a a
overload int 97 1
总结:为什么隐式转换适用于重载但不适用于看似等效的模板特化?
最佳答案
当编译器看到这个时:
f('a', 1);
它无法推断类型,因为它有两个选择:
f(const char &, const char &);
f(const int &, const int &);
因为您的模板对两个参数都有共同的类型。
这两种选择都同样有效,没有合理的规则来解决这种歧义。因此编译器必须报告错误以避免意外行为。请注意,静默类型转换对此问题没有影响,而且您对模板的特化也无助于解决此问题。
std::max
也会出现同样的问题。
现在的问题是,您确定第二个参数更重要并且应该对模板参数类型产生影响吗?如果是,那么您可以强制忽略第一个参数的类型(免责声明:这是不寻常且意外的,因此它可能是 future 代码维护者容易出现的错误)。
template <typename T>
struct Identity {
using type = T;
};
// note C++20 introduces std::type_identity
template<typename T>
void f(const typename Identity<T>::type& x, const T& y)
{
std::cout << "generic " << x << " " << y << std::endl;
}
在这种形式中,第一个参数不会参与模板的类型推导,因为它取决于第二个参数的类型。
现在这个
f('a', 1);
将编译,因为第二个参数将导致 T=int
并且第一个参数类型应与第二个参数相同。现在可以执行静默转换。
关于c++ - 模板特化中的隐式转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55900098/