我有这个简单的代码:
class A{};
class B : public A{};
class C : public B{};
class Test
{
public:
template<typename T>
void f(T&){printf("template\n");}
void f(A&){printf("specialization\n");}
};
int main()
{
A a;
B b;
C c;
Test test;
test.f(a);
test.f(b);
test.f(c);
}
当我运行它(VS2010)时,我有这个输出:
specialization
template
template
是否可以使用A-derived
类进行调用以使用专门化?
答案 0 :(得分:20)
是的,这是可能的,但您必须稍微更改一下代码。
首先,要技术性,第二个函数f()
不是模板函数的特化,而是重载。解析重载时,为所有类型不是A
的参数选择模板版本,因为它是完美匹配:T
推导为等于参数的类型,因此在调用{时例如,在类型推导之后,编译器必须在以下两个重载之间进行选择:
f(b)
当然,第一个是更好的匹配。
现在,如果您希望在使用void f(B&){printf("template\n");}
void f(A&){printf("specialization\n");}
的子类参数调用函数时选择第二个版本,则必须使用一些SFINAE技术来防止函数模板在正确实例化时被正确实例化。类型A
被推断为T
的子类。
您可以将A
与std::enable_if
类型特征结合使用来实现这一目标。
std::is_base_of
以下是如何在完整的程序中使用它:
// This will get instantiated only for those T which are not derived from A
template<typename T,
typename enable_if<
!is_base_of<A, T>::value
>::type* = nullptr
>
void f(T&) { cout << "template" << endl; }
修改强>
如果您使用的编译器不完全符合C ++ 11(因此不支持函数模板上的默认模板参数),您可能希望更改{{1}模板重载的定义如下:
#include <type_traits>
#include <iostream>
using namespace std;
class A{};
class B : public A{};
class C : public B{};
class D {};
class Test
{
public:
template<typename T,
typename enable_if<!is_base_of<A, T>::value>::type* = nullptr
>
void f(T&) { cout << ("template\n"); }
void f(A&){ cout << ("non-template\n");}
};
int main()
{
A a;
B b;
C c;
D d;
float f;
Test test;
test.f(a); // Will print "non-template"
test.f(b); // Will print "non-template"
test.f(c); // Will print "non-template"
test.f(d); // Will print "template"
test.f(f); // Will print "template"
}
程序的行为将是相同的。请注意,如果f()
的返回类型为template<typename T>
typename enable_if<!is_base_of<A, T>::value, void>::type
f(T&) { cout << ("template\n"); }
,则可以省略f()
类模板的第二个参数。