user8044236
user8044236

Reputation:

Why dynamic_cast(this) in constructor not working and how to make it work?

I am using CRTP to move common implementation of D1 and D2 into template, however, when constructing object I have to make call to overloaded function specific to each type.

The output of the following piece of code is

D 0x7ffc7d370538
D1 0
D 0x7ffc7d370540
D2 0

while expected output is

D 0x7ffc7d370538
D1 0x7ffc7d370538
D 0x7ffc7d370540
D2 0x7ffc7d370540

Why dynamic_cast returns nullptr? How to fix this code?

#include <iostream>

template <typename Derived>
struct B {
    B();
    virtual ~B() {}
};

struct D1 : B<D1> {
};

struct D2 : B<D2> {
};

void use(D1* d) { std::cout << "D1 " << d << std::endl; }
void use(D2* d) { std::cout << "D2 " << d << std::endl; }

template <typename Derived>
B<Derived>::B() {
    std::cout << "D " << this << std::endl;
    Derived* derivedThis = dynamic_cast<Derived*>(this);
    use(derivedThis);
}

int main() {
    D1 d1;
    D2 d2;
}

The only workaround which I found is the following

#include <iostream>

template <typename Derived>
struct B {
    B();
    virtual ~B() {}
};

struct D1;
struct D2;

void use(D1* d) { std::cout << "D1 " << d << std::endl; }
void use(D2* d) { std::cout << "D2 " << d << std::endl; }

struct D1 : B<D1> {
    D1() { use(this); } # code duplication
};

struct D2 : B<D2> {
    D2() { use(this); } # code duplication
};

template <typename Derived>
B<Derived>::B() {
    std::cout << "D " << this << std::endl;
}

int main() {
    D1 d1;
    D2 d2;
}

However in my scenario it is too much code duplication to repeat call to function use. For example, there can be many derived classes, or many calls to functions similar to use.

Upvotes: 0

Views: 94

Answers (2)

Useless
Useless

Reputation: 67772

In this code

template <typename Derived>
B<Derived>::B() {

you're about to initialize a instance of B<Derived>. In practice, this is initializing either the B<D1> base-class subobject of a D1 instance, or the B<D2> base-class subobject of a D2 instance.

Neither the D1 nor the D2 object can exist until after the base-class subobject has been successfully created. This means that during creation of the base-class subobject, the derived-class object cannot yet exist, and likewise the dynamic type of your base-class object cannot yet be the derived type.

If you want constructive suggestions on how to structure your code instead, you really need to explain what use is really supposed to do.

Upvotes: 0

SergeyA
SergeyA

Reputation: 62603

The immediate cause of your troubles is the fact than base class constructors are called before derived classes are constructed. Because of that, casting through dynamic_class in constructor leads to nullptr.

There is no way around it, you can't use an object of derived class from base class constructor.

On a side note, usually it is a design smell when both runtime and compile-time polymorphism are used in the same class. Stick to either of them.

Upvotes: 5

Related Questions