Consider the following code :
struct Base1 { };
struct Base2 { };
struct Foo : Base1, Base2 {
int x;
};
Foo f;
Base1* b1 = &f; // ok, standard upcasting
Base2* b2 = &f; // ok, standard upcasting
// check that the empty base class optimization applies
assert(((void*)b1) == ((void*)&f));
assert(((void*)b2) == ((void*)&f));
assert(((void*)b1) == ((void*)b2));
Is it legal to access a pointer to foo, from b1
or b2
? e.g.
std::cout << reinterpret_cast<Foo*>(b1)->x
<< reinterpret_cast<Foo*>(b2)->x;
If it is, does the same apply to C++20's attribute no_unique_address
, assuming an implementation that makes the choice of treating [[no_unique_address]]
members as it treats empty base classes ?
e.g. both GCC and Clang, but not MSVC, verify the following currently :
struct Foo {
int x;
[[no_unique_address]] Base1 b1;
char y;
[[no_unique_address]] Base2 b2;
};
static_assert(offsetof(Foo, b1) == 0));
static_assert(offsetof(Foo, b2) == 0));
so both b1 and b2 have the address of their parent object, as can be verified here : https://gcc.godbolt.org/z/NF9ACy
You don't need reinterpret_cast
or "empty base optimization" in order to cast from a base class to a derived class. That's perfectly doable via static_cast
, so long as the pointer points to an object of the derived class type (and virtual
inheritance isn't used). This is standard C++98 stuff.
Indeed, if you wish to be technical, reinterpret_cast
may not work for this. reinterpret_cast
ing between two pointers works as if by doing a cast to a void*
between them. So the address will be the same. And yes, if the address of the base and derived classes happen to be the same, the address will be preserved. But as far as the C++ object model is concerned, that's not good enough.
Now, a pointer to a base class is pointer interconvertible to a derived class, but only if both classes are standard layout. This is because such pointers are pointer-interconvertible.
But empty-base-optimization does not require standard layout types. Though a type which is standard layout is required to use EBO, a type can benefit from EBO while violating the rules of standard layout types. offsetof
only applies to standard layout types, of course.
So as far as the text of your question as asked, that is not guaranteed to work.
Which brings us to no_unique_address
. The rules for pointer-interconvertibility say something explicitly about base/derived classes of standard layout type. And it has a specific statement about the first non-static data member (NSDM) of a standard layout type (which is pointer-interconvertible with the containing object). But all other NSDMs are not mentioned, and therefore are not pointer-interconvertible.
And therefore, a reinterpret_cast
, isn't good enough.