Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is converting a reinterpret_cast'd derived class pointer to base class pointer undefined behavior?

Have a look at is simple example:

struct Base { /* some virtual functions here */ };
struct A: Base { /* members, overridden virtual functions */ };
struct B: Base { /* members, overridden virtual functions */ };

void fn() {
    A a;
    Base *base = &a;
    B *b = reinterpret_cast<B *>(base);
    Base *x = b;
    // use x here, call virtual functions on it
}

Does this little snippet have Undefined Behavior?

The reinterpret_cast is well defined, it returns an unchanged value of base, just with the type of B *.

But I'm not sure about the Base *x = b; line. It uses b, which has a type of B *, but it actually points to an A object. And I'm not sure, whether x is a "proper" Base pointer, whether virtual functions can be called with it.

like image 421
geza Avatar asked Apr 05 '19 08:04

geza


People also ask

What is a pointer to a derived class?

A pointer to derived class is a pointer of base class pointing to derived class, but it will hold its aspect. This pointer of base class will be able to temper functions and variables of its own class and can still point to derived class object. Writing code in comment?

What is pointer in C++?

Pointer is a data type that stores the address of other data types. The pointer of Base Class pointing different object of derived class: A derived class is a class which takes some properties from its base class. It is true that a pointer of one class can point to other class, but classes must be a base and derived class, then it is possible.

Does a reinterpret_cast change the value of a pointer?

Assuming that alignment requirements are met, a reinterpret_cast does not change the value of a pointer outside of a few limited cases dealing with pointer-interconvertible objects:

How do you use reinterpret cast in C++?

reinterpret_cast in C++ | Type Casting operators. reinterpret_cast is a type of casting operator used in C++. It is used to convert one pointer of another pointer of any type, no matter either the class is related to each other or not. It does not check if the pointer type and data pointed by the pointer is same or not.


2 Answers

static_cast (or an implicit derived-to-base-pointer conversion, which does exactly the same thing) is substantially different from reinterpret_cast. There is no guarantee that that the base subobject starts at the same address as the complete object.

Most implementations place the first base subobject at the same address as the complete object, but of course even such implementations cannot place two different non-empty base subobjects at the same address. (An object with virtual functions is not empty). When the base subobject is not at the same address as the complete object, static_cast is not a no-op, it involves pointer adjustment.

There are implementations that never place even the first base subobject at the same address as the complete object. It is allowed to place the base subobject after all members of derived, for example. IIRC the Sun C++ compiler used to layout classes this way (don't know if it's still doing that). On such an implementation, this code is almost guaranteed to fail.

Similar code with B having more than one base will fail on many implementations. Example.

like image 177
n. 1.8e9-where's-my-share m. Avatar answered Oct 16 '22 21:10

n. 1.8e9-where's-my-share m.


The reinterpret_cast is valid (the result can be dereferenced) if the two classes are layout-compatible; that is

  • they both have standard layout,
  • they both have the same non-static data members

But the classes do not have standard layout because one of the requirements of StandardLayoutType it that the class has no virtual functions or virtual base classes.

Regarding the validity of pointers derived from conversions, the standard has this to say in the section on "Safely-derived pointers":

6.7.4.3 Safely-derived pointers

4. An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value. Alternatively, an implementation may have strict pointer safety, in which case a pointer value referring to an object with dynamic storage duration that is not a safely-derived pointer value is an invalid pointer value unless the referenced complete object has previously been declared reachable. [ Note: The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined, see 6.7.4.2. This is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value. —end note ] It is implementation-defined whether an implementation has relaxed or strict pointer safety.

like image 37
P.W Avatar answered Oct 16 '22 20:10

P.W