Стандартный макет С++

Я проходил отличные статьи в классах С++ POD, Trivial и Standard Layout Одно свойство, которое я не ясно понял относительно стандартного макета, следующее: -

 A standard layout has no base classes of the same type as the first 
    non-static data member

Таким образом, следующее не будет стандартным макетом, так как он имеет первый член, такой же, как базовый класс

struct NonStandardLayout3 : StandardLayout1 {
    StandardLayout1 x; // first member cannot be of the same type as base
};

Но по производительности и по свойствам, как выше структура отличается от

struct StandardLayout5 : StandardLayout1 {
    int x;
    StandardLayout1 y; // can have members of base type if they're not the first   
};

который является коррекцией выше этого.

Ответы

Ответ 1

Причина в том, что стандартные типы макетов эффективно определяют "пустую оптимизацию базового класса", когда базовые классы без каких-либо элементов данных не занимают места и имеют тот же адрес, что и первый член данных (если он есть) производного класса.

Однако попытка сделать это, когда базовый класс имеет тот же тип, что и первый член данных, нарушает модель памяти С++, которая требует, чтобы разные объекты одного и того же типа имели разные адреса.

Из ISO/IEC 14882: 2011 1.8 [intro.object]/6:

Два объекта, которые не являются битовыми полями, могут иметь один и тот же адрес, если один является подобъектом другого, или если хотя бы один является подобъектом базового класса с нулевым размером и они имеют разные типы; в противном случае они должны иметь разные адреса.

эффективно передавая пустой базовый класс, 9.2 [class.mem]/20:

Указатель на объект структуры стандартного макета, соответствующим образом преобразованный с помощью reinterpret_cast, указывает на его начальный член (или если этот элемент является битовым полем, а затем блоку, в котором он находится) и наоборот.

Было бы невозможно, чтобы следующие типы (Type1 и Type2) были совместимы с макетами (хотя в противном случае они были бы стандартными классами макета) без этого ограничения.

struct S1 {};
struct S2 {};

struct Type1 : S1 {
    S1 s;
    int k;
};

struct Type2 : S1 {
    S2 s;
    int m;
};