Why does CRTP not cause infinite nesting?
Asked Answered
B

2

7

I am confused about how CRTP is compiled. If we have something like this:

template<class T>
class Base
{

};
class Derived : public Base<Derived>
{

};

Why doesn't something akin to this happen during compilation?

(X[Y] denotes X inherits from Y)

Upon declaration of a Derived instance Derived d;

d is being expanded into an infinite recurrence of templates and inheritances

d[Base<Derived[Base<Derived[Base<Derived[Base<Derived[...]>]>]>]>]

Why does this not happen? All tutorials on CRTP only explain what you can do with it, not what happens under the hood (at least vaguely).

Breda answered 3/10, 2016 at 3:11 Comment(3)
In the context of Base, T is not a fully complete type. Because of this, you can't access things like typedefs defined in T from Base (see 1 and 2). Since T isn't a fully complete type, the compiler doesn't have to infinitely recurse in this situation. At least that's my understanding.Bonded
@Eichhörnchen Because I derive from Base, which is templated by Derived, which inherits from Base, which is templated by Derived, which inherits from Base...Breda
@Bonded yea sounds about rightBreda
D
7

The fundamental concept to understand is that an instance of a template is just a class. It is fundamentally no different than any other class.

When you have a typical template definition:

template<typename T> class Base;

Then the template instance:

Base<int>

Is just a class of that name. It has nothing to do with int, and has absolutely no relationship, whatsoever, with an int. It doesn't inherit from it, in some way, or in any way.

Next step:

class Derived;

template<typename T> class Base {};

Base<Derived> foo;

Again, Base<Derived> is just a class. It has no intrinsic relationship with Derived. It doesn't derive from it, does not inherit from it, nothing.

So, now we take the final step:

template<class T>
class Base
{

};

class Derived : public Base<Derived>
{

};

This declares a class called Derived, which inherits from a class called Base<Derived>. And Base<Derived> is just a class. It's a very simple class. Can't get any simpler than that. It doesn't have any methods. It doesn't have any members. Nor private, protected, or public. It's a tiny class, but it has the same rights and privileges as any other class. You can declare a pointer to it. Or a reference to it. It's just a class.

Again, the key concept is that an instance of a template is just a class. It doesn't "inherit" in any way from the class that's the parameter to the template. In this case, the template instance is completely empty, but it can do anything that any other class can do. It can have public, private, and protected members. It can derive from some other class, which could be another template instance (since a template instance is just a class). Or, some other class can derive from the template instance, because a template instance is just a class.

There is no infinite nesting here. You just have one class inheriting from another class. The second class happens to be a template instance, but did I happen to mention that a template instance is just a class?

Diocese answered 3/10, 2016 at 3:22 Comment(0)
M
3

Simple explanation: because the following is completely valid:

template<class T>
class Base
{
};

class Derived /* at this point, Derived is declared (not defined) */;

int main()
{
    Base<Derived> base;  // the definition of Derived (or lack thereof) is irrelevant.
}
Monotony answered 3/10, 2016 at 3:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.