[英]Template specialization vs. Function overloading
问题很简单。 之间有什么区别:
template <typename T>
T add(T a, T b)
{
return a + b;
}
template <>
int add<int>(int a, int b)
{
return a + b; //no reason to specialize, but still...
}
和:
template <typename T>
T add(T a, T b)
{
return a + b;
}
int add(int a, int b)
{
return a + b; //no reason to overload, but still...
}
他们似乎是一样的。
拳头变体更简单,因为编译器将必须收集然后从仅包含add<int>(int, int)
专项的单个项的候选集合中进行选择。 第二种变体将通过收集一组包含两个项目的候选集add<int>(int, int)
和add(int, int)
并使用花哨函数重载排名算法在它们之间进行选择来使编译器执行一些额外的工作。
除了在重载解析的执行方式上有所不同之外,模板特化可将您锁定为签名。 意味着您不能这样做:
template <>
long add<int>(int a, int b)
// ^- the return type is long , not int
{
return a + b; //no reason to specialize, but still...
}
因为声明(和函数签名)是由主模板决定的,所以编译器会抱怨返回类型的更改。 进行重载解析时,编译器甚至不考虑专业化。 它只是从主实例实例化一个声明,并且该声明必须与最终调用的内容匹配。
因此,重载在如何定义重载方面更加灵活。 而且编译器没有歧义。 如果参数类型匹配,则重载解析机制会优先使用非模板而不是模板生成的函数。 通常,重载比专门化功能模板要容易得多。
在这种情况下没有什么区别,但是在某些情况下会有所不同。 让我们看一下这个示例代码
template<class T> // (a)
void f(T);
template<> // (b)
void f<>(int*);
template<class T> // (c)
void f(T*);
int main()
{
int *p;
f(p);
}
当我们调用f(p);
,您希望调用什么函数? 大多数人会选择b
,这是错误的。 其原因是因为专业化不适用于重载解析。 它们是模板的特殊配方,它告诉编译器如果您推断出这种类型,则以这种方式将模板标记出来。
那么编译器确实是经过重载决议,并说我可以叫a
与T
是int*
,或可致电我c
与T
是int
。 由于后者更专业 ( T
较窄),因此c
获胜。 这可能是非常令人惊讶的,但是一旦您忽略了专业化而只考虑了重载,它就变得很有道理。
沃尔特·布朗(Walter E. Brown)提供了有关模板和专业化的非常深入的视频,称为“ C ++函数模板:它们如何真正起作用?” ,您应该考虑观看。 这是一个小时的时间,但是非常详细。
在“简单”情况下,它们的行为将相同,但是在某些情况下它们会有所不同。
一个简单的区别是显式调用add<int>
,它将调用专门化而不是重载。
对于重载解决方案,还存在其他差异,例如:
add(42, 4.2f);
将因模板专门化而失败( int
和float
,因此无法推导T
)
而重载有效,并将float
转换为int
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.