Recently I found a case where is was easier to do a template specialization instead of real inheritance. The derived classes just had to implement a pure virtual function and had no own members. It was something like:
#include <iostream>
class Interface {
public:
virtual void calculate() = 0;
virtual float getResult() = 0;
};
class Base : public Interface {
float result;
public:
Base() : result(1) {};
virtual ~Base() {};
virtual void calculate();
virtual float getValue() = 0; // do some very complex calculation here
float getResult() { return result; }
};
class DerivedA : public Base {
public:
DerivedA() : Base() {};
~DerivedA() {};
float getValue();
};
class DerivedB : public Base {
public:
DerivedB() : Base() {};
~DerivedB() {};
float getValue();
};
void Base::calculate() {
for (int i = 0; i < 10; i++)
result += getValue();
}
float DerivedA::getValue() {
return 1;
}
float DerivedB::getValue() {
return 1.1;
}
int main() {
Interface * a = new DerivedA();
a->calculate();
Interface * b = new DerivedB();
b->calculate();
std::cout << "Result A: " << a->getResult() << std::endl;
std::cout << "Result B: " << b->getResult() << std::endl;
delete a;
delete b;
}
This can be written as specialized templates as:
#include <iostream>
class Interface {
public:
virtual void calculate() = 0;
virtual float getResult() = 0;
};
template<typename T>
class Base : public Interface {
float result;
public:
Base() : result(1) {};
void calculate();
float getValue(); // do some very complex calculation here
float getResult() { return result; };
};
typedef Base<int> DerivedA; // actually int and float are only examples
typedef Base<float> DerivedB; // and may be some much more complex types!
template<typename T>
void Base<T>::calculate() {
for (int i = 0; i < 10; i++)
result += getValue();
}
template<typename T>
float Base<T>::getValue() {
return 0;
}
template<>
float Base<int>::getValue() {
return 1;
}
template<>
float Base<float>::getValue() {
return 1.1;
}
int main() {
Interface * a = new DerivedA();
a->calculate();
Interface * b = new DerivedB();
b->calculate();
std::cout << "Result A: " << a->getResult() << std::endl;
std::cout << "Result B: " << b->getResult() << std::endl;
delete a;
delete b;
}
Both examples give the same results, I guess the second one is faster as no virtual tables need to be evaluated (the methods getValue() may even be inlined in the second case).
So my question is: What are the limits of using template specialization instead of inheritance? Are there any side effects I haven't seen? Any benefits of inheritance over template specialization? I know that I can't create new members and methods for specialized classes as I can do for derived. But can this be a general, more performant approach for such use cases where I only need to implement some type-specific code?
Btw: Is there a name for this pattern?
Templates and Inheritance are not interchangeable.
Templates express static polymorphism (ie polymorphism at compile-time)
Inheritance allows runtime polymorphism : you can manipulate Base
class pointers and expect the runtime to call the correct virtual function for you.
Whit your templates approach, what if you want to manipulate a container of Base<>
objects (eg std::vector<Base<??>>
) and call calculate()
on them ? You can't.
So although both inheritance and templates express interfaces and polymorphism, they really are different beasts: choosing one over the other depend on your context, and how your types are going to be used.
Note:
Performance considerations should not alter this choice
When using superclasses and subclasses, either DerivedA or DerivedB can be passed to a non-templated function or a method that takes a Base class instance.
void method(Base &base)
{
// ...
}
The major limitation of the template-based approach is that this is no longer possible. method() will also have to be a template:
template<typename T>
void method(Base<T> &base)
{
// ...
}
If method() is large, there's going to be quite a code bloat, here.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.