Lets say I have two types A and B.
Then I make this type
struct Pair{
A a;
B b;
};
Now I have a function such as this.
void function(Pair& pair);
And lets assume that function
will only ever use the a
part of the pair.
Then is it undefined behavior to use and call the function in this way?
A a;
function(reinterpret_cast<Pair&>(a));
I know that a compiler may insert padding bytes after a member but can it also do it before the first member?
I think it's defined behavior, assuming Pair
is standard-layout. Otherwise, it's undefined behavior.
First, a standard layout class and its first member share an address. The new wording in [basic.compound] (which clarifies earlier rules) reads:
Two objects a and b are pointer-interconvertible if:
* [...]
* one is a standard-layout class object and the other is the first non-static data member 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
(5.2.10).
Also from [class.mem]:
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).
So the reinterpret_cast
from A
to Pair
is fine. If then function
only ever access the a
object, then that access well-defined, as the offset of A
is 0, so the behavior is equivalent to having function
take an A&
directly. Any access to the b
would be undefined, obviously.
However, while I believe the code is defined behavior, it's a bad idea. It's defined behavior NOW, but somebody someday might change function
to refer to pair.b
and then you're in a world of pain. It'd be a lot easier to simply write:
void function(A& a) { ... }
void function(Pair& p) { function(p.a); }
and just call function
directly with your a
.
Yes, it's undefined behaviour.
In a struct pair, there can be padding between a and b. An assignment to a member of a struct can modify any padding in the struct. So an assignment to pair.a can modify memory where it thinks there is padding in the struct, where in reality there is just random memory following the memory occupied by your a.