Consider the following example:
#include <type_traits>
#include <cstddef>
class A1 {
private:
int i;
char ch;
};
class B1 {
public:
[[no_unique_address]] A1 a;
char ch;
};
static_assert(std::is_standard_layout_v<B1>);
class A2 {
public:
int i;
char ch;
};
class B2 {
public:
[[no_unique_address]] A2 b2;
char ch;
};
static_assert(std::is_standard_layout_v<B1>);
static_assert(offsetof(B1, ch) != offsetof(B2, ch));
Godbolt: https://godbolt.org/z/1o91bE
Following the standard wording A1 and A2 are layout compatible [1]. Similarly B1 and B2 are layout compatible. However the ch member in B1 has different offset than the ch member in B2.
A1 is not POD for the purpose of layout while A2 is, this affects the offset of the ch member.
Two possible directions to fix this:
- Modify the standard to disallow
[[no_unique_address]] non-empty non-static data members to be part of a common initial sequence (or maybe only allow as the last element of a common initial sequence).
- Modify the ABI to disable overlapping of
[[no_unique_address]] non-empty data members in standard-layout classes.
Personally I would prefer the first option of these two.
[1] https://eel.is/c++draft/class#mem.general-22
Consider the following example:
Godbolt: https://godbolt.org/z/1o91bE
Following the standard wording
A1andA2are layout compatible [1]. SimilarlyB1andB2are layout compatible. However thechmember inB1has different offset than thechmember inB2.A1is not POD for the purpose of layout whileA2is, this affects the offset of thechmember.Two possible directions to fix this:
[[no_unique_address]]non-empty non-static data members to be part of a common initial sequence (or maybe only allow as the last element of a common initial sequence).[[no_unique_address]]non-empty data members in standard-layout classes.Personally I would prefer the first option of these two.
[1] https://eel.is/c++draft/class#mem.general-22