tom tom - 4 years ago 86
C++ Question

Unnecessary usage of virtual functions just for code reuse

I'm using inheritance only for the sake of code reuse, I never cast class to its base class. It is performance heavy program so I would like to avoid the use of

virtual
functions but I do not know how. Consider following code

class A{
public:
void print(){
std::cout << "Result of f() is: " << f() << std::endl;
}

virtual std::string f(){
return "A";
}
};

class B : public A{
public:
virtual std::string f(){
return "B";
}
};


Would it be somehow possible to not use
virtual
function for function
f()
and not to reimplement function
print()
in the class
B
? I do not care that
A
is base class of
B
I just do not want to write down
f()
again. Probably inheritance is not the way to go, maybe templates can be used in smart way but I have no idea.

Answer Source

The CRTP pattern is typically used to avoid dynamic dispatch when it can be statically determined what implementation method to call for a particular method.

In your example, both A and B would inherit from a single base class, which provides the print() method. The base class, let's call it Print, is a template whose template argument is a class that provides f(). The twist that earned this pattern the "curious" moniker is that the subclasses must inherit the base class templatized over the subclass. This allows the subclasses to access the base class's print method, but obtaining a version of the base class - and by extension the version of print - that invokes their own f.

Here is an example of working code:

#include <iostream>

template<typename F>
class Print {
public:
  void print() {
    F& final = static_cast<F&>(*this);
    std::cout << "Result of f() is: " << final.f() << std::endl;
  }
};

class A: public Print<A> {
public:
  std::string f(){
    return "A";
  }
};

class B: public Print<B> {
public:
  std::string f(){
    return "B";
  }
};


int main() {
  A a;
  B b;
  a.print();
  b.print();
}

Although the print implementation is reused among A and B, there are no virtual methods here, nor is there virtual (run-time) dispatch or run-time checks. The one cast present is a static_cast<> whose safety is duly verified by the compiler. If the methods in question can be inlined, the compiler is free to do so.

However, there is no inheritance either - note that B is not derived from A (if it were, the trick wouldn't work), and you cannot pass B& where an A& is expected. The trading of run-time dispatch, with its benefits and drawbacks, for compile-time dispatch, is a fundamental property of static polymorphism.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download