Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does C++ guarantee that the address of a Base subobject will be the same as the address of its Derived object in case of single inheritance?

Here is a piece of code:

struct Base
{
    virtual void Foo() = 0;
    virtual ~Base() { }
};

struct Derived : Base
{
    virtual void Foo() override { }
    unsigned long long some_new_data_members[42];
};

int test_offset()
{
    Derived object{};
    Base* base_subobject = &object;

    return reinterpret_cast<unsigned char*>(base_subobject) - reinterpret_cast<unsigned char*>(&object);
}

The function test_offset always returns 0 on all versions of gcc, clang and msvc compilers that I have checked.

My question is: does the C++ standard guarantee that this function will be always returning 0 in case of single inheritance between Base and Derived? If it does not, can somebody provide a real-life example (maybe with some exotic platform) where this code will return something different from 0?

Please do not answer about multiple inheritance!


upd.:

As pointed out by user VTT, code like this can lead to a non-zero offset:

struct Base
{
};

struct Derived : Base
{
    virtual void Foo() { }
};

It yields an offset equal to the size of a pointer on msvc.

But the fundamental difference of this example from my code snippet is that the Derived class here introduces some new virtual functions. Thus, a new vtable pointer has to appear somewhere in Derived, and it might be placed before the Base subobject, giving us a non-zero offset.

So, now I want to ask more specifically about my scenario. What if the Base class has already had some virtual functions, and Derived just overrides them without adding any new ones (although it might add some new non-static member fields). Are such non-zero offsets possible in this particular case?

like image 754
Taras Avatar asked Aug 14 '19 11:08

Taras


1 Answers

Property of the pointers to different objects to have the same value is called pointer-interconvertible. The standard lists all possible cases when this is guaranteed:

6.7.2 Compound types [basic.compound]
4 Two objects a and b are pointer-interconvertible if:
(4.1) they are the same object, or
(4.2) one is a union object and the other is a non-static data member of that object (12.3), or
(4.3) one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object (12.2), or
(4.4) there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast (8.5.1.10). [Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. —end note ]

Example:

struct Base{};

struct Derived : Base
{
    virtual void Foo() { }
};

offset will be 8 (on typical 64-bit platform) for extra vtable pointer added by Derived infront of Base class subobject.

like image 118
user7860670 Avatar answered Nov 06 '22 02:11

user7860670