I see some strange code in our project like follows.I test it and get the right answer.But I think it is illegal,Can anyone explain this to me?
class Member
{
public:
Member():
a(0),b(1)
{}
int a;
int b;
};
// contains `Member` as its first member
class Container
{
public:
Container():
c(0),d(0)
{}
Member getMemb(){return fooObject;}
Member fooObject;
int c;
int d;
};
and how we use it:
int main()
{
auto ctain = new Container;
auto meb = (Member *)ctain; // here! I think this is illegal
cout << "a is " << meb->a << ", b is" << meb->b << endl;
return 0;
}
but I get the right answer, a is 0 and b is 1.Is this just a coincidence?I also noted that if fooObject
is not the first member, I will get a wrong answser.
The snippet is legal. The C style cast (Member*)
here is effectively a reinterpret_cast
. From [basic.compound]
Two objects
a
andb
are pointer-interconvertible if:
they are the same object, or
one is a union object and the other is a non-static data member of that object, or
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, or [...]
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
.
Special care should be taken to make sure it is indeed a standard layout type, possibly with a static_assert(std::is_standard_layout_v<Container>)
On the other hand, you could sidestep this entire fiasco if you just wrote auto meb = &ctain.fooObject;
It's not exactly coincidence, it happens that your fooObject is the first member of your Container class, so the beginning of it will rest at the same starting address as the Container object. If you do:
size_t s = offsetof(Container, Container::fooObject);
It will tell that your fooObject offset will be 0, which start where your Container object start in terms of memory, so when you cast to a Member pointer it's pointing to the correct address. But for instance in other cases you would be in big trouble for sure:
class Container
{
public:
Container() : c(0),d(0) {}
Member getMemb(){return fooObject;}
int c; // fooObject isn't first member
Member fooObject;
int d;
};
Or was a virtual class, because virtual classes store an pointer for lookup into a table.
class Container
{
public:
Container() : c(0),d(0) {}
virtual ~Container() {} // Container is virtual, and has a vtable pointer
// Meaning fooObject's offset into this class
// most likely isn't 0
Member getMemb(){return fooObject;}
Member fooObject;
int c;
int d;
};
Someone else will have to tell you whether this cast is legal even in your example, because I'm not sure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With