23

Are these 2 structs layout-compatible?

struct One {
    float x, y, z;
};

struct Two {
    float c[3];
};

Both contains 3 floats, so in a way, this description can be considered true (from N3797):

16 Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).

N4659 has a different text:

The common initial sequence of two standard-layout struct (Clause 12) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types and either neither entity is a bit-field or both are bit-fields with the same width.

Two standard-layout struct (Clause 12) types are layout-compatible classes if their common initial sequence comprises all members and bit-fields of both classes (6.9).

If the answer is no, they are not layout-compatible, then: was it the intention of the committee? Maybe they do want One and Two to be layout-compatible (maybe a committee member reads this, and can clarify).


Bonus question: is it guaranteed, that sizeof(One)==sizeof(Two)?

geza
  • 28,403
  • 6
  • 61
  • 135
  • 4
    They may each contain 3 floats but the first one has 3 members and the second one only has one. That said `std::complex` is allowed to do this so I'm not sure – NathanOliver Jul 26 '17 at 16:13
  • the size would be the same – Ryan Jul 26 '17 at 16:13
  • Thinking about **alignment**. ..if it's greater than 4 bytes **then no**. But you can force it (in a not portable way). As-is compiler is free to do what's best (according to its optimization target and architecture restrictions) – Adriano Repetti Jul 26 '17 at 16:16
  • 1
    @NathanOliver -- `std::complex` is part of the standard library, and standard library implementors have to make it work. That can involve non-portable and non-standard techniques that ordinary programmers should not have to deal with. – Pete Becker Jul 26 '17 at 16:18
  • @PeteBecker Good point. – NathanOliver Jul 26 '17 at 16:33
  • Here is the case I'm envisioning breaking this (it would probably never happen but AFAIK the standard allows it). You could have a compiler that decides it wants to make `One` 16 bytes wide. To do that it adds 4 bytes of padding between `x` and `y` (or `y` and `z`) instead of at the end. This would mean `y` and `z` no longer line up with `c[1]` and `c[2]`. – NathanOliver Jul 26 '17 at 16:36
  • @NathanOliver A slightly less unrealistic scenario: the compiler (perhaps based on the presence of an array member) increases one type's alignment. This pretty much automatically means they must not be layout-compatible: if they were layout-compatible, `struct S1 { char c; One d; }` and `struct S2 { char c; Two d; }` would also be layout-compatible, but they wouldn't be very likely to have `d` at the same offset. –  Jul 26 '17 at 16:47
  • @hvd: Why? I think most implementations put `d` at the same offset. – geza Jul 26 '17 at 17:14
  • @geza Implementations tend to put it at an offset equal to the alignment requirement (how else would you ensure it's properly aligned?), so if that is different for the two structs, so are the offsets. –  Jul 26 '17 at 17:16
  • @hvd: why would alignment be different for the two structs? They both contain 3 floats. – geza Jul 26 '17 at 17:17
  • @geza Please re-read my first comment here then. I posed that as a less unrealistic alternative to NathanOliver's hypothetical implementation. Less unrealistic is still unrealistic. But it's less unrealistic because there are real implementations where specific types have a different alignment depending on whether they're a member of a struct, and it's just possible on such an implementation that being an indirect member of a struct doesn't qualify. –  Jul 26 '17 at 17:20
  • @hvd: Sorry, I understand now. What you say is not unrealistic at all. It is quite realistic. – geza Jul 26 '17 at 17:26
  • *To do that it adds 4 bytes of padding between x and y (or y and z) instead of at the end.* if different *c++* compilers will be use different layout for standard-layout structs, will be impossible use such structs in binary communications between different modules and os. huge count of os api use pointers to standard-layout structs(and unions). this can work only if our compiler use the same layout for struct (used in api call) that os compiler use. i absolute sure that answer to question is wrong - these 2 structs layout-compatible. and `sizeof(One)==sizeof(Two)`. – RbMm Dec 31 '18 at 00:55
  • if this does not formally follow from the documentation, then the question is about the quality of the documentation. why not. and the question can (and should be expanded). if `T` standard-layout struct or fundamental type (like `int`) - are `T` and `struct T2 { T t; }` layout-compatible ? `struct T3 : T {}` ? – RbMm Dec 31 '18 at 00:55

2 Answers2

13

Well, no:

[...] if they have the same number of non-static data members [...]

One has three members: x, y, and z. Two has one member: c. They don't have the same number of non-static data members, therefore they aren't layout compatible.


The new wording is different but you end up at the same place. [basic.types] defines layout-compatible as:

Two types cv1 T1 and cv2 T2 are layout-compatible types if T1 and T2 are the same type, layout-compatible enumerations, or layout-compatible standard-layout class types.

[class.mem] defines layout-compatible classes are:

Two standard-layout struct types are layout-compatible classes if their common initial sequence comprises all members and bit-fields of both classes ([basic.types]).

Where the common initial sequence is:

The common initial sequence of two standard-layout struct types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types and either neither entity is a bit-field or both are bit-fields with the same width.

Here, the first member of One (float x) is not layout-compatible with the first member of Two (float c[3]), so the common initial sequence is empty.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • But the new wording use the word "entity", not member. I can correlate "entities" here. x is c[0], y is c[1], z is c[2]. Am I on a wrong track? – geza Jul 26 '17 at 17:00
  • I could treat `c[0]` as a value entity. Why did they use the word entity here? They could have used member, but for some reason, they choose entity. I'm not saying that you're wrong, it is just not convincing enough. Maybe the text can be made more precise. – geza Jul 26 '17 at 17:11
  • @geza No, you couldn't. `c[0]` isn't a value. The word "entity" is necessary to include both members and bitfields. – Barry Jul 26 '17 at 17:22
  • Thank you, I understand now. – geza Jul 26 '17 at 17:30
3

The compiler is allowed to add padding between members in a class or struct.

Array elements are in contiguous locations.

They may not be layout compatible depending on how the compiler organizes the members in the struct.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • [*Standard-layout classes are useful for communicating with code written in other programming languages.*](http://eel.is/c++draft/class.prop#6) if assume that different *c++* compilers can use different layout for Standard-layout (`struct One`) structs - will be impossible any binary communication between modules compiled with this 2 different compiler. impossible use function like `void fn(One*)` implemented in module compiled with one compiler and called from another - if they will be use different binary layout of *One* – RbMm Dec 29 '18 at 14:09