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.