What's the purpose of layout-compatible types?
Asked Answered
P

2

9

The standard defines when two types are layout-compatible. But, I don't see anywhere in the standard what the consequences are when two types are layout-compatible. It seems that layout-compatible is a definition which is not used anywhere.

What is the purpose of layout-compatible?

Note: Supposedly, it could mean that the types have the same layout (offsetof is the same for each corresponding member), so for example, for trivially copyable types, underlying bytes can be copied between them. But I don't see something like this in the standard.

Perennial answered 29/10, 2018 at 8:40 Comment(10)
Doesn't the common initial sequence guarantee count as using this defintion? Albeit transitivly...Ceresin
the only thing I could find is §12.2.21 defining common initial sequence and §6.7.2.3 "Pointers to layout-compatible types shall have the same value representation and alignment requirements" (n4713)Lamberto
@StoryTeller: If they have the common initial sequence, then what's the consequences? Is there any property that these types have, and we can utilize?Perennial
#21956854 from the answer there (high rep user) "AFAICT the standard doesn't actually say what can and can't be done with layout-compatible types." Maybe ... a dupe..Lamberto
@bolov: you're right, the definition is indeed used at pointers to layout-compatible types (maybe that's the answer...). But I've expected some stronger property for layout-compatible types.Perennial
@geza: An interesting proposal for checking layout compatibility. This was initiated because of code which relied on layout compatibility. open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0466r2.pdfUntwine
It is important for casting. If objects of classes are not layout-compatible then you need to spin the reinterpret_cast wheel of fortune. Also tells a compiler writer how he needs to implement inheritance, an upcast needs to be valid. The requirement to have standard-layout as well is notable, classes with virtual methods are not. A practical issue due to the presence of the v-table pointer. Compilers have to do extra work to make a cast valid, commonly called an "adjustor thunk". Such practical considerations are omitted from the standard too often.Readus
@HansPassant: I don't really see, what layout-compatible gives as an extra. If alignment requirements are the same, then reinterpret_casting should be safe already (I don't see any difference whether they are layout-compatible or not). The only thing I see what bolov found: "Pointers to layout-compatible types shall have the same value representation and alignment requirements".Perennial
@P.W: I wonder what that code was: "a bit of code that relied on two types being layout-compatible".Perennial
@geza: I too wondered. It had to involve a reinterpret_cast. If I had known for sure, I would have posted an answer. :)Untwine
Q
9

The standard does define one specific case where layout compatibility matters: in unions. If two members are layout-compatible, and one of them is the active union member, then you may access that object through pointers/references to any layout-compatible member of that union. This is a consequence of the "common initial sequence" rule.

Quadrangular answered 29/10, 2018 at 13:23 Comment(3)
Thanks for the info! For further reference, this is the rule which says what you described.Perennial
Furthermore, because of this rule, isn't it the case, that what I've described (offsetof is the same for each corresponding member, the layout must be the same) must be true, even if it not said explicity? I mean, if the layout differs, then the union rule cannot be satisfied.Perennial
The funny thing is that enums with the same underlying type are layout-compatible and you're kinda allowed to inspect common initial sequence if members of structs are 2 layout-compatible enums, except that you'll have UB because of violation of strict aliasing ruleFierro
E
2

The Standard makes no attempt to mandate that all implementations be suitable for all purposes. Consequently, quality implementations intended to be suitable for purposes beyond those for which the Standard require support will generally need to extend the semantics of the language. One of the simplest and most useful ways they can do this is by saying that in some circumstances where portions of the Standard define or imply the behavior of some action but another part says an overlapping category of actions invoke UB, they will process the behavior as defined or implied by the former parts. On many compilers, for example, there is an option (typically enabled with a -fno-strict-aliasing flag) to say that any program whose behavior would be defined in the absence of type-access rules will be processed in that fashion, even if those rules would say the program invokes UB.

While there are relatively few situations where the fact that two structures are layout-compatible would cause behavior to be defined by the Standard when it otherwise wouldn't, there are many situations where it would imply how an implementation must behave in the absence of those type-access rules (by making it essentially impossible for an implementation to do anything else). For example, if structure types T1 and T2 are layout compatible, that would suggest that if a pointer to a T1 is converted to a T2*, any operation upon a member of the structure using the latter pointer will access the corresponding member of the T1 object.

Because not all programs need such abilities, the Standard does not require that all implementations provide them. On the other hand, implementations that are suitable for low-level programming will provide means by which parts of the code that are designed to handle one type can be used to handle layout-compatible types interchangeably, whether the Standard requires them to or not (implementations that don't would simply be limited to uses other than low-level programming).

I think the Standard would be enormously improved by officially recognizing categories of implementations that are suitable for low-level programming and others that make no claim to be, rather than trying to define a single set of behavior for all implementations. Nonetheless, defining concepts like "layout compatibility" greatly improves the range of constructs that will be portable among implementations that are suitable for low-level programming.

Eichman answered 29/10, 2018 at 16:34 Comment(3)
Or in short, when a compiler encounters undefined behaviour, it is entirely legal for it to just plain define the behaviour. ;PTsushima
@JustinTime: If an implementation defines the behavior of some actions, but the Standard classifies an overlapping category of actions as violating a runtime constraint (and thus invoking Undefined Behavior), it may not be clear whether the former definition is supposed to apply in cases that violate the constraint. Thus, the significance of the second sentence in my answer.Eichman
I know, I was just summing it up (with tongue slightly in cheek) by pointing out that when a compiler encounters undefined behaviour, providing its own definition (in place of the official "it's undefined") as a compiler extension is a valid response.Tsushima

© 2022 - 2024 — McMap. All rights reserved.