这个问题更理论化,范围不同于:
Template Specialization VS Function Overloading-解释了编译器甚至在查看特化之前就进行了重载解析
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; //Specialization
}
int add(int a, int b)
{
return a + b; //Overloading
}
add(3,4); // in Main
1.何时使用函数完全特化以及何时使用函数重载?
为什么要使用模板特化,因为模板被解析了两次(在模板定义和实例化时)?
另一方面,在函数重载的情况下,答案似乎很明显:当您有特定的特定情况需要使用不同的逻辑而不是模板通用逻辑时,请使用此方法。
2.如果形式相同,每次重载候选对象时是否都会选择查找过程(针对gcc或clang)? 形式上的意思是:函数名称,参数数量,参数类型。
在全功能特化的情况下,当模板函数为候选函数时,编译器将选择模板实例。根据接受的转换选择一些(顺序:严格完全匹配,资格调整,继承到虚拟的基本转换)。
在函数重载的情况下,在候选函数中,为调用选择可行的函数。在可行的功能中,为通话选择最佳匹配。基本上,编译器检查转换强度(按顺序:严格完全匹配,资格调整,整数/ float 促销,转换,用户转换(例如强制转换))。
通常,如果在模版(特化)和非模版(重载)之间存在最佳可行性的模棱两可的情况下,编译器会选择非模版。但为什么?查找机制如何工作?
一个因素可能是支持的转换不同的事实。
例:
template <typename T>
bool isEqual(const T& a, const T& b); //generic form
template <>
bool isEqual(const string& a, const string& b); //specialization
bool isEqual(const string& a, const string& b); //overloading
bool c = isEqual ("cheers", "noroc"); //in Main, the arguments are const char *
在这种情况下,特化将不匹配,因为它将需要用户定义的转换const char *->字符串,这在自变量推导上下文中是禁止的。
另一方面,由于用户定义的转换在此有效,因此重载匹配。
但是如果在Main中将字符串作为参数呢?
string s1, s2;
bool c = isEqual (s1, s2);
在这种情况下,编译器为何选择重载函数?
最佳答案
最根本的区别是,重载是通过名称查找独立地找到的,而专门化是通过原始模板本身来找到的。结果是仅由 ADL 找到 call 后出现的重载:
template<class T> void f(T) {} // #1
template<class T> void g(T t) {f(t);}
void f(int) {} // #2
template<> void f(char) {} // #3
namespace N {
struct A {};
void f(A) {} // #4
}
void h() {
f(1); // calls #2
g(1); // calls #1
g('1'); // calls #3
g(N::A()); // calls #4
}
尽管重载解析首选一个函数,而不是具有相同签名的功能模板特化,但只有显式特化完全阻止隐式实例化主模板(可以使用
f<>('a')
进行选择)。另一个重要的(也是最著名的)区别是,重载的函数模板的行为与部分专长(函数模板不可用)非常相似。这甚至扩展到选择多个匹配项(通过部分排序)时选择最佳过载。当然,名称查找受到限制,因此,这不是表示使用后可能出现的自定义的好方法;组合这些功能(并启用模板参数推导)的惯用方式是拥有一个“负责人”功能模板,该模板将调用转发到类模板的适当特化(可能是从部分特化生成的)。
关于c++ - 何时使用功能完全特化而不是重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61717021/