通过重载实现部分模板专用化



我创建了一个简单的round模板函数,该函数带有一个额外的模板参数,用于定义舍入值在返回之前需要强制转换的类型。

template <typename T, typename U>
T round(U val) {
T result;
if (val >= 0)
result = (T)(floor(val + (U)(.5)));
else
result = (T)(ceil( val - (U)(.5)));
return result;
}
int a = round<int>(5.5); // = 6
// no compiler warnings

但我也希望可以保留额外的模板参数,这样就不必插入已经添加的类型作为参数。

template <typename T>
T round(T val) {
return round<T>(val);
}
double b = round(5.5) // = 6.0
// C2668

然而,现在编译器抱怨道:

错误C2668:对重载函数的调用不明确

我以为编译器总是会选择最具体的模板,应该是最后一个。为什么情况并非如此,是否有任何变通方法(不是专门针对此循环功能)?


不明确的调用不是指向round(5.5),而是指向return round<T>(val);。因此,这个问题的答案是将重载函数的返回值重写为

return round<T,T>(val);

这解决了问题。

感谢galop1n对我另一个问题的回答

您得到了一个错误,因为在模板参数推导过程中没有推导出返回类型。相反,它们是从推导的函数自变量中替换出来的。因为两个重载都推导出了相同的参数,所以重载资源是不明确的,这会导致编译器错误。

在C++11中,您可以为函数模板定义一个默认的模板参数。如果您添加了一个与默认返回值相等的额外默认函数参数,那么您将始终将参数类型作为返回类型,除非您显式传递默认返回值:

#include <iostream>
#include <cmath>
#include <type_traits>
template <typename T, typename Ret = T>
Ret xround(T val, Ret ret = Ret()) {
return static_cast<Ret>(
(val >= 0) ?
floor(val + (T)(.5)) :
ceil( val - (T)(.5))
);
}
int main()
{
auto a = xround(5.5, int()); // = 6
static_assert(std::is_same<decltype(a), int>::value, "");
std::cout << a << "n";       
auto b = xround(5.5); // = 6.0
static_assert(std::is_same<decltype(b), double>::value, "");
std::cout << b << "n";
}   

实时示例

请注意,我使用了三元运算符而不是if-else,并且我将函数重命名为xround,因为在C++11中,<cmath>中已经有一个round(当然也可以使用)。

注意:临时与标记调度类似:它完全用于确定返回类型,实际的临时应该由编译器进行优化。

您的问题不是模板专业化,而是过载模糊性。

这是类似的:

int fn(int) { return 0; }
// error: new declaration ‘double fn(int)’
// error: ambiguates old declaration ‘int fn(int)’
double fn(int) { return 0; }

使用一个U将T作为默认参数的模板不会更好:

template <typename T, typename U = T>
T fn(U val) {
return T();
}
int main() {
// error: no matching function for call to ‘fn(double)’
// note: template argument deduction/substitution failed:
double d = fn(1.5); // fn<double>(1.5) will work
}

部分专业化是不允许的:

template <typename T, typename U>
T fn(U val) {
return T();
}
// error: function template partial specialization ‘fn<T, T>’ is not allowed
template <typename T>
T fn<T, T>(T val) {
return T();
}

最新更新