Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template specialization and derived classes in C++

Tags:

c++

templates

I have this simple code:

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);
}

When I run it(VS2010) I have this output:

specialization
template
template

Is it possible to make the calls with A-derived classes to use specialization?

like image 559
Mircea Ispas Avatar asked Feb 10 '13 11:02

Mircea Ispas


People also ask

What is meant by template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is a class template in C?

A class template provides a specification for generating classes based on parameters. Class templates are generally used to implement containers. A class template is instantiated by passing a given set of types to it as template arguments.

What is the difference between class and template?

An individual class defines how a group of objects can be constructed, while a class template defines how a group of classes can be generated. Note the distinction between the terms class template and template class: Class template. is a template used to generate template classes.

Can you derive template classes?

Class Template Inheritance in C++ This is because inheritance is only possible with a class, and a template is not a class unless it is instantiated by passing some data type to it.


1 Answers

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.

like image 118
Andy Prowl Avatar answered Sep 28 '22 03:09

Andy Prowl