2
votes

I have used the CRTP pattern for a while, however reading answers about undefined behaviour related to downcasting I don't understand why the static_cast<Derived&>(this), where this is of type Base<Derived>* is defined behaviour, where Derived inherits publicly from Base<Derived>.

The standard is quite clear:

static_cast < new_type > ( expression )

If new_type is a pointer or reference to some class D and the type of expression is a pointer or reference to its non-virtual base B, static_cast performs a downcast. This downcast is ill-formed if B is ambiguous, inaccessible, or virtual base (or a base of a virtual base) of D.

But looking at similar questions, C++ inheritance downcasting:

How do I cast from a point object [Base class] to a subpoint object [Derived class]?

Top rated answer:

"You can't; unless either point [Base] has a conversion operator, or subpoint [Derived] has a conversion constructor, in which case the object types can be converted with no need for a cast."

Another example: static_cast parent class to child class C++

// B : public A
A a;
B* bptr = static_cast<B*>(&a);

Top rated comment:

"It's undefined behavior."

But then says:

"You can do it safely using either CRTP or dynamic_cast."

Here (C++ static_cast downcast validity) it is again mentioned:

Base base{190};
A& a = static_cast<A&>(base);

"No [it is not valid], an object of type Base is not an object of type A [Derived]"


How is the downcast performed in CRTP different; why does it not cause undefined behaviour but the cases above do? Following the logic of the answer above, is it not true to say that class Base<Derived> is not an object of type Derived (the converse it true), yet you can use static_cast?

Perhaps I'm simply misunderstanding CRTP.

1
You're mixing up "the objects" and "pointers to the objects".user253751

1 Answers

2
votes

Revisiting the first paragraph:

If new_type is a pointer or reference to some class D and the type of expression is a pointer or reference to its non-virtual base B, static_cast performs a downcast. This downcast is ill-formed if B is ambiguous, inaccessible, or virtual base (or a base of a virtual base) of D.

and including the next sentence:

Such static_cast makes no runtime checks to ensure that the object's runtime type is actually D, and may only be used safely if this precondition is guaranteed by other means, such as when implementing static polymorphism.

CRTP uses/is another name for static polymorphism.

Given struct D : B<D> if B<D> static_cast's *this to D&, then this is defined behaviour because the this pointer really is a pointer to both a D and a B<D>.