Henk
Henk

Reputation: 846

Static polymorphism with final or templates?

What are the advantages and disadvantages of using something like

template <typename TData>
class Base {
public:
   void foo()
   {
       static_cast<TData*>(this)->doFoo();
   }
};

class Derived : public Base<Derived>
{
    void doFoo() { \*...*\ }
};

instead of

class Base {
  virtual ~Base = default;
  virtual void foo() = 0;
};

class Derived {
  void foo() final { \*...*\ }
};

?

As far as I understand it, both approaches avoid vtable-lookups at runtime, right? So in which situations should one use the first method containing more boilerplate?

Upvotes: 1

Views: 301

Answers (1)

StPiere
StPiere

Reputation: 4243

Static polymorphism (first case) via CRTP is done at compile time and I would basically prefer it, if all classes are known at compile time. Note also that the Base itself is not a class but a class template, so Base is not a base class of Derived.

It's not just performance but also a design decision of your project if should use first or second.

In the second case Base is a base of Derived with polymorphical behaviour, so for ex. method calls on derived can be called through a pointer to Base, for ex.:

Base* b = new Derived();
b->foo(); // calls Derived::foo via vtable lookup.

Depending how foo() is invoked, compiler can devirtualize it or not, for ex:

Base* b = new Derived();
b->foo(); // cannot be devirtualized

Derived* d = new Derived();
d->foo(); // probably can be devirtualized, because compiler knows 
          // via final that none can override foo,
          // so it doesnt need to consult vtable.

Basically I prefere everything what can be done at compile time, not just because of performance, but also robustness - runtime errors are harder to handle. But it's also design decision - if the first method makes your project more complicated and small performance penalty doesn't matter, then you can go very well with second approach.

There is no "one fit's all" solution.

Upvotes: 2

Related Questions