Reputation: 4114
When I would like to use a function (here called do_some_work()
/do_some_templated_work()
)for several different types of input classes and I do not want to rewrite that function several times, according to my knowledge I can either template that function, or derive all involved classes from a base class, and use a pointer of that base class. I wrote a short test program for both cases:
#include <iostream>
#include <string>
class BaseClass {
public:
BaseClass(){
class_name = std::string("Base class");
}
virtual ~BaseClass(){};
virtual double work_func(const double a, const double b) {
(void) a;
(void) b;
return 0;
};
virtual void print_class_name(void) {};
private:
std::string class_name;
};
class DerivedClassA : public BaseClass{
public:
DerivedClassA(){
class_name = std::string("Class A");
}
~DerivedClassA() override {
}
double work_func(const double a, const double b) override{
return a + b;
}
void print_class_name(void) override{
std::cout << class_name << '\n';
}
private:
std::string class_name;
};
class DerivedClassB : public BaseClass{
public:
DerivedClassB(){
class_name = std::string("Class B");
}
~DerivedClassB() override {
}
double work_func(const double a, const double b) override{
return a - b;
}
void print_class_name(void) override{
std::cout << class_name << '\n';
}
private:
std::string class_name;
};
void do_some_work(BaseClass &test_class){
test_class.print_class_name();
std::cout << test_class.work_func(5, 6) << '\n';
}
template <class T>
void do_some_templated_work(T &test_class) {
test_class.print_class_name();
std::cout << test_class.work_func(5, 6) << '\n';
}
int main()
{
std::cout << "Hello World!" << std::endl;
DerivedClassA AClass;
DerivedClassB BClass;
do_some_work(AClass);
do_some_work(BClass);
do_some_templated_work(AClass);
do_some_templated_work(BClass);
return 0;
}
When looking at the ASM code I did not see an immediate advantage of either (which could be related to the compilation switches, though). Therefore, are there any things I did not consider here, and do both approaches have their advantages/disadvantages when comparing both? Or is there a third approach I could use for the same purpose?
Upvotes: 0
Views: 29
Reputation: 23711
In general, the first option involves virtual dispatch (i.e. jumping to the right function at run-time) while the second knows the right function to call at compile-time already. The latter generally has less overhead and opens up more optimization opportunities for the compiler, but there can be drawbacks (code size, instruction cache etc.). Performance will always depend on particulars, so if you care about it, profile.
Something that you couldn't do with inheritance out-of-the-box is e.g. returning different types of values from work_func
- this is where templates shine.
On the other hand, inheritance (especially when following the Liskov Substitution Principle) can make the contract/expectations of the interface much more clear (void do_some_templated_work(T &test_class)
does not tell you that T
needs to implement e.g. print_class_name
).
Upvotes: 1