c++static-cast

Base to Derived static_cast, not visible relation


We can static_cast from Base* to Derived*, but in my case the relationship is not visible when the cast is supposed to happen. Is there any trick (besides of reinterpret_cast of course) to make it work?

struct Derived;

struct Base 
{
    Base()
    {
        //Is there a way to make it work?
        //Derived* d = static_cast<Derived*>(this);
    }
};

struct Derived: public Base
{
};

int main()
{
    Base b{};
    // Here it works as the compiler is aware of the relationship
    Derived* d = static_cast<Derived*>(&b);
}

You can try it here.

Update: Thanks for your comments. Of course, I know that my example code is broken, I just wanted to demonstrate the problem. The real world problem is much more complex.

I have an opaque pointer that points to either Base or Derived (compile time switch). And I need that opaque pointer in the constructor of the Base class. When the opaque pointer is Derived* then Base cannot be instantiated just through Derived, so it is 100% safe. In the other case Base == Derived so no cast would happen.

As it is an opaque pointer and we are in Base, we have only a forward declaration of Derived. Like:

#ifdef PLATFORM_A
    typedef struct Derived UsedType;
#else
    typedef struct Base UsedType;
#endif

So I would need an UsedType* in the Base constructor, the virtual this is either Base* or Derived*. Unfortunately if we are not on PLATFORM_A then Derived is not defined at all, so when the code reaches Base's constructor the compiler cannot know about it's relation with Derived (as Derived might not exist at all, and in that case the cast is from Base* to Base*).

Update 2: Real world code

Update 3: I updated the code once more. Now there is a member that stores the opaque this pointer. I don't use it in the constructor, I use it only outside. It needs to be initialized in the constructor (as the member is const).

#include <iostream>

#define PLATFORM_A

#ifdef PLATFORM_A
    typedef struct Derived UsedType;
#else
    typedef struct Base UsedType;
#endif

struct User 
{
    UsedType* const platform;
    void print() const;
};

struct Base 
{
    const char* const name{"Base"};
    User const iNeedThis;
#ifdef PLATFORM_A
  protected:
#endif
    Base()
      : iNeedThis{(UsedType*)this}
      //: iNeedThis{static_cast<UsedType*>(this)}
    {
    }
};

#ifdef PLATFORM_A
  struct Derived: public Base
  {
    const char* const name{"Derived"};
    Derived()
      : Base()
    {
    }
  };
#endif

void User::print() const
{
    std::cout << platform->name << std::endl;
}

int main()
{
    UsedType myObj{};
    myObj.iNeedThis.print();
}

https://godbolt.org/z/sMr5Kfxrn


Solution

  • I mean, it's trivial to do literally what you asked, even without moving the constructor:

    struct Base 
    {
      UsedType* self();
    
    #ifdef PLATFORM_A
      protected:
    #endif
        Base()
        {
            UsedType* iNeedThis = self();
        }
    };
    

    Later on, in base.cpp or wherever:

    #ifndef PLATFORM_A
      UsedType* Base::self() { return this; }
    #endif
    

    and even later on, in derived.cpp

    #ifdef PLATFORM_A
    UsedType* Base::self()
    {
      return static_cast<Derived*>(this);
    }
    #endif
    

    But it still seems like a bad design.