C++ Academy · Lesson

Static Polymorphism

Avoid virtual call overhead.

Lesson 2 of 413 steps

Static Polymorphism is a free C++ Academy lesson on CoddyKit. This is lesson 2 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the C++ Academy learning path, and your progress syncs across the web and the CoddyKit app. The C++ Academy course includes 4 lessons in total.

Two Kinds of Polymorphism

C++ supports two flavors of polymorphism.

  • Dynamic: virtual functions resolved at runtime via vtable
  • Static: templates/CRTP resolved at compile time

Static polymorphism trades flexibility for speed.

The Cost of virtual

A virtual call requires an indirect jump through the vtable. The compiler usually cannot inline it, blocking many optimizations.

#include <iostream>

struct Shape {
    virtual double area() const = 0;
    virtual ~Shape() = default;
};

struct Square : Shape {
    double s;
    Square(double x) : s(x) {}
    double area() const override { return s * s; }
};

int main() {
    Shape* p = new Square(3);
    std::cout << p->area() << "\n";
    delete p;
    return 0;
}

Static Dispatch with CRTP

With CRTP the base calls the derived method directly through a compile-time cast, so the compiler can inline everything.

#include <iostream>

template <typename T>
struct Shape {
    double area() const { return static_cast<const T*>(this)->area(); }
};

struct Square : Shape<Square> {
    double s;
    Square(double x) : s(x) {}
    double area() const { return s * s; }
};

int main() {
    Square sq(3);
    std::cout << sq.area() << "\n";
    return 0;
}

Forwarder Naming

When the base and derived both define area, give the base forwarder a distinct name like compute so it unambiguously calls into the derived implementation.

#include <iostream>

template <typename T>
struct Shape {
    double compute() const { return static_cast<const T*>(this)->area(); }
};

struct Circle : Shape<Circle> {
    double r;
    Circle(double x) : r(x) {}
    double area() const { return 3.14159 * r * r; }
};

int main() {
    Circle c(2);
    std::cout << c.compute() << "\n";
    return 0;
}

Templates as Static Polymorphism

Plain function templates are also static polymorphism: the same code works for any type providing the required interface (duck typing at compile time).

#include <iostream>

struct Dog { void speak() const { std::cout << "Woof\n"; } };
struct Cat { void speak() const { std::cout << "Meow\n"; } };

template <typename Animal>
void make_speak(const Animal& a) { a.speak(); }

int main() {
    make_speak(Dog{});
    make_speak(Cat{});
    return 0;
}

When the Type Is Known

Static polymorphism applies only when the concrete type is known at compile time. If you must store heterogeneous objects in one container and choose behavior at runtime, you still need virtual functions.

No Heterogeneous Containers

You cannot put Shape<Square> and Shape<Circle> in the same std::vector as a common base, because they are unrelated types. This is the central limitation of static polymorphism.

Performance Benefit

By inlining the dispatched call, the compiler can fold constants and eliminate function-call overhead entirely. In tight loops over a single known type, static polymorphism can be substantially faster.

#include <iostream>

template <typename T>
struct Op {
    int apply(int x) const { return static_cast<const T*>(this)->apply(x); }
};

struct Doubler : Op<Doubler> {
    int apply(int x) const { return x * 2; }
};

int main() {
    Doubler d;
    int sum = 0;
    for (int i = 0; i < 5; ++i) sum += d.apply(i);
    std::cout << sum << "\n";
    return 0;
}

Combining Both

You can use CRTP for the hot path and a thin virtual wrapper when type erasure is needed. This gives speed where it matters and flexibility where required.

Static Interface Checks

If a derived class forgets to implement the required method, the error appears at compile time when the base forwarder is instantiated, not at runtime.

#include <iostream>

template <typename T>
struct Greeter {
    void greet() const { static_cast<const T*>(this)->hello(); }
};

struct English : Greeter<English> {
    void hello() const { std::cout << "Hello\n"; }
};

int main() {
    English{}.greet();
    return 0;
}

Choosing the Right Tool

Use static polymorphism when:

  • The type is known at compile time
  • Performance is critical
  • You do not need a common runtime base type

Otherwise prefer virtual functions for clarity and flexibility.

Quick Check

Pick the key trade-off of static polymorphism.

Recap

You compared static and dynamic polymorphism.

  • Virtual = runtime dispatch, vtable, heterogeneous containers
  • CRTP/templates = compile-time dispatch, inlinable, faster
  • Static needs the type known at compile time
  • Choose based on flexibility vs performance needs
Free to start

Learn C++ with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
51
Lessons
203

Frequently Asked Questions

Is the “Static Polymorphism” lesson free?

Yes — any 3 lessons of the C++ Academy learning path, including “Static Polymorphism”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The C++ Academy course includes 4 lessons in total.

What will I learn in “Static Polymorphism”?

Avoid virtual call overhead. You practise C++ Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C++ Academy?

No prior experience is required. C++ Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.

How long does the “Static Polymorphism” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C++ Academy lesson?

Yes. Every C++ Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The CRTP Idiom
  2. Static Polymorphism
  3. Mixins with CRTP
  4. When to Use CRTP
← Back to C++ Academy