0

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.

3
  • Why do you want this? Commented May 20, 2021 at 15:28
  • @TedLyngmo This is so called curiously recurring template pattern (CRTP) Commented May 20, 2021 at 15:34
  • 1
    @TedLyngmo Of course, you are right. I typed too quickly. I'll modify the original post. The problem I think still exists. Commented May 20, 2021 at 15:37

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, you are absolutely right. I stated the "recursion" problem which is not there. Many thanks for the explanation. I think there is a gap in my understanding of incomplete types which confused me and made me ask the original question. I'll read your answer more thoroughly in the meantime. Again, thanks for the detailed explanation. I'll accept the answer and may be come up with a follow up one if needed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.