TruLa
TruLa

Reputation: 1147

CRTP pattern with variable Base

Assume that one has two base classes Base1 and Base2 for which CRTP pattern is desired.

template <typename TDerived>
class Base1 {
};

template <typename TDerived>
class Base2 {
};

Now I would like to define a Derived class such that it can be "parametrized" with a base one. What would be the proper way to define it in C++ (C++17 if that matters)?

Below is a pseudo-C++ code

template <template <typename> class TBase>
class Derived : public TBase<Derived<TBase>> {}; // Recursion problem here

In reality such a derived class is an extension that is applicable to any base class.

Upvotes: 0

Views: 88

Answers (1)

Guillaume Racicot
Guillaume Racicot

Reputation: 41770

From your comment:

The problem I think still exists.

Consider your own pseudo-code when fed to a compiler (and some addition):

template <typename TDerived>
struct Base1 {
    auto get() const -> int {
        return static_cast<TDerived const*>(this)->a;
    }
};

template <typename TDerived>
struct Base2 {
    auto get() const -> int {
        return static_cast<TDerived const*>(this)->b;
    }
};

template <template<typename> typename TBase>
struct Derived : TBase<Derived<TBase>> {
    int a;
    int b;
};

auto main() -> int {
    auto b = Derived<Base1>{};

    b.a = 1;
    b.b = 2;

    return b.get();
}

Live example

As you can see, that pattern is no problem for the compiler.

Now I would like to define a Derived class such that it can be "parametrized" with a base one. What would be the proper way to define it in C++ (C++17 if that matters)?

The way you posted it in the question is the right way, and works all the way to C++98.


You seem to state that there's a recursion problem here, but there is not.

You have a template class Derived with a template template parameter Base1. The compiler now "knows" that this type Derived<Base1>. It is incomplete though, until the } is reached.

Then the compiler sees the base, which is TBase<something>, which is Base1 with some template parameter. Base1 need to be instantiated. The something is Derived<Base1>, which is an incomplete type.

The compiler then instantiate Base1 with Derived<Base1>. During that process, you cannot use Derived<Base1> in a way that it would require it to be complete.

Now that the base is instantiated and a complete type, the compiler finishes to instantiate Derived<Base1>, now complete.

There is no recursion here.

Upvotes: 3

Related Questions