Search code examples
c++crtppure-virtualvirtual-functions

Protect CRTP pattern from stack overflowing in "pure virtual" calls


Consider the following standard CRTP example:

#include <iostream>

template<class Derived>
struct Base {
    void f() { static_cast<Derived *>(this)->f(); }
    void g() { static_cast<Derived *>(this)->g(); }
};

struct Foo : public Base<Foo> {
    void f() { std::cout << 42 << std::endl; }
};

int main() {
    Foo foo;
    foo.f(); // just OK
    foo.g(); // this will stack overflow and segfault
}

If this was regular virtual inheritance I could have mark virtual f and g methods as pure like

struct Base {
    virtual void f() = 0;
    virtual void g() = 0;
};

and get a compile time error about Foo being abstract. But CRTP offers no such protection. Can I implement it somehow? Runtime check is acceptable too. I thought about comparing this->f pointer with static_cast<Derived *>(this)->f, but didn't manage to make it work.


Solution

  • Here is another possibility:

    #include <iostream>
    
    template<class Derived>
    struct Base {
        auto f() { return static_cast<Derived *>(this)->f(); }
        auto g() { return static_cast<Derived *>(this)->g(); }
    };
    
    struct Foo : public Base<Foo> {
        void f() { std::cout << 42 << std::endl; }
    };
    
    int main() {
        Foo foo;
        foo.f(); // just OK
        foo.g(); // this will not compile
    }
    

    For GCC, it gives a pretty clear error message ("error: use of 'auto Base::g() [with Derived = Foo]' before deduction of 'auto'"), while for Clang, it gives a slightly less readable infinitely recursing template instantiation of Base<Foo>::g, with g instantiating itself but eventually ending in an error.