Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a pointer allowed to change value in single inheritance?

I know that with multiple inheritance the value of a pointer is allowed to change. But is that the case with single inheritance too? Or with POD types for that matter?

You probably know the classic example:

#include <iostream>
using std::cout;
struct Base1 { virtual void f1() {} };
struct Base2 { virtual void f2() {} };
struct Derived : public Base1, public Base2 { virtual void g() {} };
int main() {
    Derived d{};
    auto *pd = &d;
    auto pb1 = (Base1*)pd;
    auto pb2 = (Base2*)pd;
    cout << pd << "\n"; // say, 0x1000
    cout << pb1 << "\n"; // say, 0x1000
    cout << pb2 << "\n"; // say, 0x1008 !!!
}

So far so good and that is good old compiler practice. The objects are layed out in a way that the "root" of Base2 has an offset to Derived which can be printed when looking at the actual pointer value. This not a problem when refraining from doing void* and reinterpret_casts.

And as far as I know in practice this occurs only with multiple inheritance.

But my question: What does the standard say about "pointer changing values during cast"? Can this happen only with multiple inheritance or can that also happen with single inheritance, or casting PODs?

like image 700
towi Avatar asked Feb 24 '17 20:02

towi


People also ask

Which defines a single inheritance?

The inheritance in which a single derived class is inherited from a single base class is known as the Single Inheritance. It is the simplest among all the types of inheritance since it does not include any kind of inheritance combination or different levels of inheritance.

What is single inheritance in C++?

C++ Single Inheritance Single inheritance is defined as the inheritance in which a derived class is inherited from the only one base class.

How does Java circumvent single inheritance?

Java uses the single inheritance model to simplify class inheritance. To circumvent the single inheritance restriction, Java introduces a new construct, called interface.

What is single and multiple inheritance?

Meaning and Definition. In the case of single inheritance, the derived class performs the inheritance of a single base class. In the case of multiple inheritance, the derived class can acquire multiple base classes. Use of Features. The derived classes can utilize the features that belong to a single base class.


2 Answers

If the type is standard-layout, then a pointer to the containing struct at any level of inheritance is equal to a pointer to the first element. By transitivity, the pointers to all levels of inheritance are also the same -- and the Standard goes ahead and also makes this guarantee in the odd case when no non-static data members exist.

(Note that to be standard-layout, a type can introduce data members at only one level in the inheritance diagram -- further subclasses can add functions and remain standard layout, but cannot add more data).

If the type is not standard-layout, then no guarantees are made and you must use static_cast, not reinterpret_cast (nor reinterpret_cast equivalents such as multiple static_cast with void* as an intermediate) to properly include any offset.

All POD types are of course standard-layout, PODness requires both standard-layout and triviality.

The exact rule I'm explaining is found in section 9.2:

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any).

[ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note ]

[ Note: The object and its first subobject are pointer-interconvertible. — end note ]

The rule that explicitly states that odd layouts are possible for types which are non standard-layout is in section 10:

The order in which the base class subobjects are allocated in the most derived object is unspecified.

like image 189
Ben Voigt Avatar answered Oct 03 '22 22:10

Ben Voigt


Is a pointer allowed to change value in single inheritance?

I am not able to find anything in the standard that would prevent an implementation from doing that.

I found the following in the C++11 standard that are related to conversion of pointers between pointers to base classes and derived classes:

4.10 Pointer conversion

3 A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

and

5.2.9 Static cast

2 An lvalue of type “cv1 B,” where B is a class type, can be cast to type “reference to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D. The result has type “cv2 D.” An xvalue of type “cv1 B” may be cast to type “rvalue reference to cv2 D” with the same constraints as for an lvalue of type “cv1 B.” If the object of type “cv1 B” is actually a subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the result of the cast is undefined. [ Example:

struct B { };
struct D : public B { };
D d;
B &br = d;
static_cast<D&>(br); // produces lvalue to the original d object

end example ]


Given

class B { ... };
class D : public B { ... };

An implementation is free to choose the following layout:

+------------------+
|  D members       |
+------------------+
|  B members       |
+------------------+

as long as it does the right thing when converting pointers and references.

In such an implementation,

D d;
D* dptr = &d;
B* bptr = &d;

The values of dptr and bptr are going to be different.

like image 41
R Sahu Avatar answered Oct 03 '22 22:10

R Sahu