More about Dynamic and Static Polymorphism


In my last post "Dynamic and Static Polymorphism", I introduced dynamic polymorphism. Today, I continue with static polymorphism and present are very interesting idiom in C++: curiously recurring template pattern (CRTP).


A short recap. This is where I left in my last post.

Dynamic Polymorphism is based on object orientation and enables us to separate between the interface and the implementation of a class hierarchy. To get late dynamic dispatch, you need two ingredients: virtuality and an indirection such as a pointer or a reference. The following program exemplified dynamic polymorphism:

// dispatchDynamicPolymorphism.cpp

#include <chrono>
#include <iostream> auto start = std::chrono::steady_clock::now(); void writeElapsedTime(){ auto now = std::chrono::steady_clock::now(); std::chrono::duration<double> diff = now - start; std::cerr << diff.count() << " sec. elapsed: "; } struct MessageSeverity{ virtual void writeMessage() const { std::cerr << "unexpected" << '\n'; } }; struct MessageInformation: MessageSeverity{ void writeMessage() const override { std::cerr << "information" << '\n'; } }; struct MessageWarning: MessageSeverity{ void writeMessage() const override { std::cerr << "warning" << '\n'; } }; struct MessageFatal: MessageSeverity{}; void writeMessageReference(const MessageSeverity& messServer){ // (1) writeElapsedTime(); messServer.writeMessage(); } void writeMessagePointer(const MessageSeverity* messServer){ // (2) writeElapsedTime(); messServer->writeMessage(); } int main(){ std::cout << '\n'; MessageInformation messInfo; MessageWarning messWarn; MessageFatal messFatal; MessageSeverity& messRef1 = messInfo; MessageSeverity& messRef2 = messWarn; MessageSeverity& messRef3 = messFatal; writeMessageReference(messRef1); writeMessageReference(messRef2); writeMessageReference(messRef3); std::cerr << '\n'; MessageSeverity* messPoin1 = new MessageInformation; MessageSeverity* messPoin2 = new MessageWarning; MessageSeverity* messPoin3 = new MessageFatal; writeMessagePointer(messPoin1); writeMessagePointer(messPoin2); writeMessagePointer(messPoin3); std::cout << '\n'; }


Static polymorphism is based on templates. Let me refactor the program using the Curiously Recurring Template Pattern (CRTP).


Rainer D 6 P2 540x540Modernes C++ Mentoring

Stay informed about my mentoring programs. Subscribe for the news.



Static Polymorphism

Before I refactor the previous program dispatchDynamicPolymorphism.cpp, here is the key idea of CRTP: A class Derived derives from a class template Base and Base has Derived as a template argument.

template <typename T>
class Base

class Derived : public Base<Derived>


Here is the pure nature of CRTP:

// crtp.cpp

#include <iostream>

template <typename Derived>
struct Base{
  void interface(){                              // (2)
  void implementation(){                        // (3)
    std::cout << "Implementation Base" << std::endl;

struct Derived1: Base<Derived1>{
  void implementation(){
    std::cout << "Implementation Derived1" << std::endl;

struct Derived2: Base<Derived2>{
  void implementation(){
    std::cout << "Implementation Derived2" << std::endl;

struct Derived3: Base<Derived3>{};             // (4)

template <typename T>                          // (1)
void execute(T& base){

int main(){
  std::cout << '\n';
  Derived1 d1;
  Derived2 d2;
  Derived3 d3;
  std::cout << '\n';


I use in the function template execute (line 1) static polymorphism. Each base invoked the method base.interface. The member function Base::interface (line 2) is the key point of the CRTP idiom. The member function dispatches to the implementation of the derived class: static_cast<Derived*>(this)->implementation().  That is possible because the method will be instantiated when called. At this point in time the derived classes Derived1, Derived2, and Derived3 are fully defined. Therefore, the method Base::interface can use the implementation of its derived classes. Pretty interesting is the member function Base::implementation (line 3). This function plays the role of a default implementation for the static polymorphism for the class  Derived3 (line 4).

Here is the output of the program:


Now, let me take the next step and refactor the program dispatchDynamicPolymorphism.cpp.

// dispatchStaticPolymorphism.cpp

#include <chrono>
#include <iostream>

auto start = std::chrono::steady_clock::now();

void writeElapsedTime(){
    auto now = std::chrono::steady_clock::now();
    std::chrono::duration<double> diff = now - start;
    std::cerr << diff.count() << " sec. elapsed: ";

template <typename ConcreteMessage>                        // (1)
struct MessageSeverity{
  void writeMessage(){                                     // (2)
  void writeMessageImplementation() const {
    std::cerr << "unexpected" << std::endl;

struct MessageInformation: MessageSeverity<MessageInformation>{
  void writeMessageImplementation() const {               // (3)
    std::cerr << "information" << std::endl;

struct MessageWarning: MessageSeverity<MessageWarning>{
  void writeMessageImplementation() const {               // (4)
    std::cerr << "warning" << std::endl;

struct MessageFatal: MessageSeverity<MessageFatal>{};     // (5)

template <typename T>
void writeMessage(T& messServer){                       
    messServer.writeMessage();                            // (6)

int main(){

    std::cout << std::endl;
    MessageInformation messInfo;
    MessageWarning messWarn;
    MessageFatal messFatal;
    std::cout << std::endl;



In this case, all concrete classes (lines 3, 4, and 5) derive from the base class MessageSeverity. The member function writeMessage is the interface that dispatches to the concrete implementations writeMessageImplementation. To achieve this, the object will be upcasted to the ConcreteMessage:  static_cast<ConcreteMessage*>(this)->writeMessageImplementation();. This is the static dispatch at compile time, and coined the name for this technique: static polymorphism.

To be honest, it took me time to get used to it, but applying the static polymorphism in line (6) is quite easy.

In the end, I want to compare dynamic and static polymorphism in a few words:

Dynamic Versus Static Polymorphism

Dynamic polymorphism happens at run time and static polymorphism at compile time. Dynamic polymorphism requires typically a pointer indirection at run time (read the post "Demystifying virtual functions, Vtable, and VPTR in C++"), but static polymorphism has no performance costs at run time. Admittedly, there is a reason why the idiom curiously recurring template pattern (CRTP) has the name curious inside. For beginners, the idiom is quite challenging to understand. So, what should you use?

First of all, don't overestimate the costs of a virtual dispatch. In most cases, you can ignore them. For the details, read the excellent paper "Technical Report on C++ Performance". It's pretty dated but has in section 5.3.3 interesting numbers about the additional costs of virtual function calls. If you are still concerned about performance, there is only one cure: measure. Put your performance tests under version control and always rerun them if something in your setup consisting of your hardware, compiler, or compiler version changes, because this invalidates your previous performance numbers.

In the end, code is way more often read the written. Therefore, you should use the techniques your team is most comfortable with.  

What's next?

Mixins are a popular technique in Python. They allow you to change the behavior of a class using multiple inheritances. Thanks to CRTP, we also have mixins in C++. Read about them in my next post.


Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, and Phillip Diekmann.


Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, Ralf Abramowitsch, John Nebel, Mipko, and Alicja Kaminska.



My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small


My special thanks to PVS-Studio PVC Logo



I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)


Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.


Contact Me

Modernes C++,




0 #1 Rud Merriam 2022-03-05 01:34
The "overhead" of a virtual function call is misleading when examined by itself as the performance report does.

What code does the virtual call replace and how does the virtual call compare to that call?

A virtual call is a compiler created decision statement. That call is replacing possibly a switch statement or a sequence of "if / else if" statements. The cost of those should be compared to the virtual call.

More directly, the impact on the overall system must be looked at. As has been oft stated, developers are not good a guessing the performance of code.
0 #2 Zole 2022-03-14 22:16
Whole code is kind of bad example for polymorphism, as in //dispatchStaticPolymorphism.cpp MessageSeverity struct code is not needed at all, and you can't use a list of MessageSeverity objects that when called writeMessageImplementation would call correct implementation as you can't have a list of MessageSeverity objects as this objects need to be template objects ....
0 #3 learnedSloth 2022-03-23 10:01
Quoting Rud Merriam:

A virtual call is a compiler created decision statement. That call is replacing possibly a switch statement or a sequence of "if / else if" statements. The cost of those should be compared to the virtual call.

I suppose there's no branch prediction for virtual calls, so it would probably depend on the predictability of those paths.
0 #4 Paul Fee 2022-04-07 16:47
The link to the "last post" at the top page leads to the top of your website, rather than the previous article.

It should be:
0 #5 Rainer 2022-05-10 16:57
Quoting Paul Fee:
The link to the "last post" at the top page leads to the top of your website, rather than the previous article.

It should be:

Thanks. Fixed


Stay Informed about my Mentoring


English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Interactive Course: The All-in-One Guide to C++20

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code


Today 4774

Yesterday 5673

Week 23348

Month 44095

All 10965556

Currently are 164 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments