Reputation: 1055
Following up a similar question in previous post about downcasting and type safety , I wonder if the following example creates undefined behavior.
An instance of Base class has been created. No dynamic binding take place.
However, in the Base::interface function the instance of the Base class is casted to an instance of the Derived class .
Is this safe? If yes , why ?
Please find the piece of code below.
#include <iostream>
template <typename Derived>
struct Base{
void interface(){
static_cast<Derived*>(this)->implementation();
}
};
struct Derived1: Base<Derived1>{
void implementation(){
std::cout << "Implementation Derived1" << std::endl;
}
};
int main(){
std::cout << std::endl;
Base<Derived1> d1;
d1.interface();
std::cout << std::endl;
}
Upvotes: 1
Views: 231
Reputation: 63144
There is no Derived
that this new Derived *
could point to, so it is definitely not safe: the cast itself has undefined behaviour, as described in [expr.static.cast]§11 (emphasis mine):
A prvalue of type “pointer to cv1
B
”, whereB
is a class type, can be converted to a prvalue of type “pointer to cv2D
”, whereD
is a complete class derived fromB
, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [...] If the prvalue of type “pointer to cv1B
” points to aB
that is actually a subobject of an object of typeD
, the resulting pointer points to the enclosing object of typeD
. Otherwise, the behavior is undefined.
You can mitigate this risk by restricting access to Base
's constructor:
template <typename Derived>
struct Base{
// Same as before...
protected:
Base() = default;
};
This is better, but still allows for the same issue if someone were to accidentally define struct Derived2 : Base<AnotherDerived> { };
. Preventing that can be done with a specific friend
declaration, but has the downside of giving full access to Base
's private members:
template <typename Derived>
struct Base{
// Same as before...
private:
friend Derived;
Base() = default;
};
Note that this still lets Derived
construct naked Base<Derived>
objects in its member functions, but that's where I generally stop whacking moles.
Upvotes: 5