I have this simple code:
class A{};
class B : public A{};
class C : public B{};
class Test
{
public:
template
void f(T&
Yes, it is possible, but you have to change your code a bit.
First of all, to be technical, the second function f()
is not a specialization of the template function, but an overload. When resolving overload, the template version is chosen for all arguments whose type is not A
, because it is a perfect match: T
is deduced to be equal to the type of the argument, so when calling f(b)
, for instance, after type deduction the compiler will have to choose between the following two overloads:
void f(B&){printf("template\n");}
void f(A&){printf("specialization\n");}
Of course, the first one is a better match.
Now if you want the second version to be selected when the function is invoked with an argument which is a subclass of A
, you have to use some SFINAE technique to prevent the function template from being correctly instantiated when the type T
is deduced to be a subclass of A
.
You can use std::enable_if
in combination with the std::is_base_of
type traits to achieve that.
// 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; }
Here is how you would use it in a complete program:
#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"
}
EDIT:
If you are working with a compiler which is not fully compliant with C++11 (and therefore does not support default template arguments on function templates), you might want to change the definition of your template overload of f()
as follows:
template<typename T>
typename enable_if<!is_base_of<A, T>::value, void>::type
f(T&) { cout << ("template\n"); }
The behavior of the program will be identical. Note that if the return type of f()
is void
, you can omit the second argument to the enable_if
class template.