compile time inheritance options

In C++, inheritance is often associated with runtime polymorphism through virtual functions. However, C++ also allows compile-time inheritance using templates. This approach can produce highly optimized code since all decisions are made at compile time, eliminating the overhead of virtual calls.

1. Using CRTP (Curiously Recurring Template Pattern)

The CRTP is a technique where a class template takes a derived class as a template parameter. This allows the base class to call methods of the derived class at compile time.

#include <iostream>

template <typename Derived>
class Base {
public:
    void interface() {
        // Call derived implementation
        static_cast<Derived*>(this)->implementation();
    }
};

class Derived1 : public Base<Derived1> {
public:
    void implementation() {
        std::cout << "Derived1 implementation" << std::endl;
    }
};

class Derived2 : public Base<Derived2> {
public:
    void implementation() {
        std::cout << "Derived2 implementation" << std::endl;
    }
};

int main() {
    Derived1 d1;
    Derived2 d2;
    d1.interface(); // prints "Derived1 implementation"
    d2.interface(); // prints "Derived2 implementation"
    return 0;
}

In this example:

2. Using Concepts (C++20)

C++20 introduced concepts, which allow you to constrain templates based on required properties or methods. This can also be used for compile-time polymorphism without virtual functions.

#include <iostream>
#include <concepts>

struct Derived1 {
    void implementation() { std::cout << "Derived1 implementation" << std::endl; }
};

struct Derived2 {
    void implementation() { std::cout << "Derived2 implementation" << std::endl; }
};

// Define a concept that requires an 'implementation' method
template <typename T>
concept HasImplementation = requires(T t) {
    { t.implementation() } -> std::same_as<void>;
};

// Generic function constrained by concept
template <HasImplementation T>
void interface(T& obj) {
    obj.implementation();
}

int main() {
    Derived1 d1;
    Derived2 d2;
    interface(d1); // prints "Derived1 implementation"
    interface(d2); // prints "Derived2 implementation"
    return 0;
}

Here:

3. Comparison

Feature CRTP Concepts
Compile-Time Check Yes Yes
Ease of Use Requires inheritance boilerplate Flexible with any type matching the concept
Function Call Member function call via base template Free function template constrained by concept
Inlining Possible Possible

Conclusion

Compile-time inheritance in C++ can be achieved using CRTP or concepts. CRTP is suitable when you want a base class to provide common functionality, while concepts offer a more flexible approach, allowing compile-time polymorphism without inheritance. Both approaches avoid virtual function overhead, enabling highly optimized code.

There's a better article here: https://www.fluentcpp.com/2020/09/11/replacing-crtp-static-polymorphism-with-concepts/


edit this page