Is a C++ object layout necessarily statically defined?
Is a C++ object layout necessarily statically defined?
More specifically, assuming A is an accessible base class of B, does the following code produce undefined behavior, and is the assertion guarenteed not to fire according to the standard?
A
B
void test(B b1, B b2)
A* a2 = &b2;
auto offset = reinterpret_cast<char*>(a2) - reinterpret_cast<char*>(&b2);
A* a1 = reinterpret_cast<A*>(reinterpret_cast<char*>(&b1) + offset);
assert(a1 == static_cast<A*>(&b1));
Edit:
I'm aware that all of the common compiler vendors implement C++ object layout (even when taking into account virtual inheritence) in a way that is compatible with the implicit assumptions of test. What I'm looking for is a guarantee (either implicit or explicit) for this behavior in the standard. Alternatively, a reasonably detailed description of the extent of object storage layout guarantees provided by the standard, as proof that this behavior is not guaranteed, will also be accepted.
test
If
A is a virtual base - no.– Ivan
Aug 25 at 14:10
A
I would be surprised if there were any guarantees provided by the standard about the above. So it may work but it falls under the "Unspecified Behavior"
– Martin York
Aug 25 at 15:04
I think that's only true for standard-layout types. For other types, the standard doesn't impose any such requirements, so implementations are allowed to do "crazy" things. Not that I know any implementations for which that
assert would fail (for any type, even with virtual inheritance).– geza
Aug 25 at 23:07
assert
Missing tag
language-lawyer– Basile Starynkevitch
Aug 28 at 7:48
language-lawyer
3 Answers
3
That may be fine. Under some specific conditions:
A is not (part of) a virtual base, or b1 and b2 have the same most derived type, or you happen to be (un-)lucky.
A
virtual
b1
b2
Edit: Your change from pass-by-reference to pass-by-value makes it trivial to show the condition above holds.
The aliasing-rules won't get in the way as the only wrong type used is char, and there is an explicit exception for that.
char
Is that really guarenteed to be true regardless of whether
A or B are standard-layout objects or anything like that?– SomeStrangeUser
Aug 25 at 14:19
A
B
makes it trivial to show the condition above holds. This statement is possible wrong. OP is asking for standard conformance. Please provide documentation for this claim.– darune
Sep 1 at 19:21
makes it trivial to show the condition above holds.
Unless eg. a standard-layout type, it is hard to see how an implementation should be restricted in this sense. Could an implementation use some kind of dynamic lookup for the base object for example ? in theory, i guess, yes. (Again, in practice i find it hard to see what the benefit should be of the offset be static and have extra overhead)
For example:
Non-static data members of a (non-union) class with the same access
control (Clause 14) are allocated so that later members have higher
addresses within a class object. The order of allocation of non-static
data members with different access control is unspecified (Clause 14).
Implementation alignment requirements might cause two adjacent members
not to be allocated immediately after each other; so might
requirements for space for managing virtual functions (13.3) and
virtual base classes (13.1).
The standard doesn't guarentee anything towards virtual base classes for example.
An object of trivially copyable or standard-layout type (6.7) shall
occupy contiguous bytes of storage.
Again, this only goes for a subset, so the standard doesn't help much here. (eg. an object with a virtual function is non-trivial to copy).
Also, see the vendor implemented macro offsetof https://en.cppreference.com/w/cpp/types/offsetof
Although for member variables only, even here, it makes it pretty clear there is not much to go on.
As you can see, most things is left to the implementation to decide.
Also see this answer(not same question, but related): C++ Standard On The Address of Inherited Members
Why do objects of standard-layout type have the same layout?
– xskxzr
Aug 30 at 12:31
They are intented to be used for communication with other programming languages.
– darune
Aug 30 at 17:29
@xskxzr: we can infer this information from the definition of
offsetof. At least, I haven't found anything else regarding this, which means that the standard doesn't describe standard-layout well, in my opinion.– geza
Sep 1 at 10:10
offsetof
No, for a reason that has nothing to do with derived classes or reinterpret_cast: The pointer arithmetic isn't guaranteed to give you back the original answer outside the context of an array. See 5.7.4-5 (expr.add) which specify when it's valid to add/subtract pointers:
When an expression that has integral type is added to or subtracted from a pointer, the result has the type
of the pointer operand. If the pointer operand points to an element of an array object, and the array is
large enough, the result points to an element offset from the original element such that the difference of
the subscripts of the resulting and original array elements equals the integral expression. ... If both the pointer operand and the result point to elements of the same array object, or one past
the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is
undefined.
The language for subtract is a bit more ambiguous, but says essentially the same thing.
True, but 3.9.4 indicates that: "The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T)." Moreover, the definition for
aligned_storage (see 20.10.7.6), seems to imply that these char objects are contiguous, i.e. forming an array: "The member typedef type shall be a POD type suitable for use as uninitialized storage for any object whose size is at most Len"– SomeStrangeUser
Sep 2 at 21:36
aligned_storage
An array object is not the same as a contiguous sequence of char objects. The former is a high-level concept defined in 8.3.4 of the standard.
– Mohan
Sep 3 at 9:35
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
It might not work if virtual inheritance is involved. Besides, all those casts probably exhibit undefined behavior somewhere by violating strict aliasing rules, but I'm too lazy to chase that down.
– Igor Tandetnik
Aug 25 at 14:08