admin管理员组

文章数量:1291037

I have a CRTP-like construct:

class Derived : public Base<Derived>

I need to calculate a pointer to a Derived object from a pointer to Base<Derived> object via reinterpret_cast. I cannot use dynamic_cast because I'm not using virtual functions here.

The types are given, I do have the pointer to the Base<Derived> object. I need the byte offset between the two types, so I can use reinterpret_cast. How do I do this (without undefined behavior)? Is it possible to do at compile-time / save as a static constexpr variable (I'm using C++20)?

I have a CRTP-like construct:

class Derived : public Base<Derived>

I need to calculate a pointer to a Derived object from a pointer to Base<Derived> object via reinterpret_cast. I cannot use dynamic_cast because I'm not using virtual functions here.

The types are given, I do have the pointer to the Base<Derived> object. I need the byte offset between the two types, so I can use reinterpret_cast. How do I do this (without undefined behavior)? Is it possible to do at compile-time / save as a static constexpr variable (I'm using C++20)?

Share Improve this question edited Feb 13 at 17:49 Remy Lebeau 598k36 gold badges503 silver badges847 bronze badges asked Feb 13 at 16:26 NewlineNewline 9735 silver badges13 bronze badges 4
  • 7 Why not just use static_cast< Derived * >( pointer_to_base ) ? – Richard Critten Commented Feb 13 at 16:32
  • @RichardCritten I though downcasting is only possible with dynamic_cast. My bad. – Newline Commented Feb 13 at 16:47
  • you only need dynamic_cast with multiple or virtual inheritance – Caleth Commented Feb 13 at 17:03
  • Not entirely sure what you are doing, but if you need to get the starting address of the object you can cast to a void pointer: dynamic_cast<void*>(ptr);. q.v. stackoverflow/a/4131099/4641116 (The other way around casting from a void pointer doesn't work.) – Eljay Commented Feb 13 at 17:19
Add a comment  | 

2 Answers 2

Reset to default 5

There is no need for offset calculations or reinterpret_cast.

If you are sure that the object is indeed a Derived one, you can simply use static_cast:

Derived derived;
Base<Derived> * pBase = &derived;

Derived * pDerived = static_cast<Derived*>(pBase);

Note that if pBase above does not actually point to a Derived object, using static_cast in the last line will invoke undefined-behavior (UB).

A side note:
As @BenVoigt commented below, it is common to add a helper method to the CRTP base class that does exactly this cast.
I.e. add something like this to Base<Derived> (replace Derived if needed with the actual template parameter name in Base):

template <typename Derived>
class Base {

    Derived * ToDerived() { return static_cast<Derived*>(this); }
    // ...
};

The CRTP pattern relies on a static_cast<> between the base class and the derived type. The Derived class type is the only derived class of the instantiated Base template type. Therefore it is guaranteed that a static_cast<Derived*>(this) is safe within the template class Base. This is why it is common to see code such as:

template<class Derived> class Value {
protected:
    Derived& derived() { return *static_cast<Derived*>(this); }
    int& value() { 
         return derived().value(); // calls Derived::value()!
    }
};

template<class Derived> class Inc : public Value<Derived> {
public:
    int operator++ () { 
        this->value()++; // calls protected Value::value()!
        return this->value();
    }
    int operator++ (int) { 
        auto r = this->value();
        ++this->value(); 
        return r;
    }
};

class obj : public Inc<obj> {
    int x = 0;
public:
    int& value() { return x; }
};
// class bad : public Inc<obj> {};
// wrong, Inc<obj> type is already an base class of obj

Here, class obj only needs to implement the base class API value(), for the intermediate base class Inc to implement an pre and post increment operators for it.

(@wohlstad got the first answer so I thought to fill in a bit more detailed answer)

本文标签: cHow to tell the byte offset between a base class and a derived classStack Overflow