#опытным
Спасибо, @kds0811, за любезно предоставленный примерчик в рамках рубрики #ЧЗХ.
Еще один сбивающий с толку примерчик, вдогонку прошлого поста:
struct Empty {};
struct Good : Empty {
int i;
char c;
};
struct Bad {
int i;
char c;
};
template <typename T>
struct S : T {
char c;
};
static_assert(sizeof(S<Good>) < sizeof(S<Bad>));
int main() {
return 0;
}Два одинаковых, казалось бы, класса Good и Bad. У них одинаковый состав полей. Только у Good есть пустой базовый класс, а у Bad - нет. Разве это как-то может повлиять на размер наследника S?
Еще как может.
Программа выше успешно компилируется aka размер
S<Good> меньше размера S<Bad>.WAT? У Good и Bad же идентичный состав полей, откуда разница?
Кстати код выше успешно компилируется только под gcc и clang, и его сборка валится на msvc. Можно посмотреть тут
Дело в том, что разница лежит уже не на уровне языка С++, а на уровне ABI(Application Binary Interface), которое поддерживает компилятор.
gcc и clang поддерживают Itanium C++ ABI, а msvc - Microsoft ABI.
Для всех компиляторов верно, что размер структур Good и Bad одинаковый(для пустой базы применяется Empty base optimization) - 8 байт.
Но как только появляется наследник S - стандарт перестает регламентировать layout объекта. Всю ответственность несет реализация.
Вы уже догадались, что речь снова зашла про наши любимые pod типы и хвостовые паддинги.
Кратко напомню. POD тип - массив или класс, не имеющий пользовательских конструкторов, приватных/защищённых нестатических данных, виртуальных функций, базовых классов и обладающий тривиальными копирующими операциями и деструктором.
struct Empty {};
struct Good : Empty {
int i;
char c;
};
struct Bad {
int i;
char c;
};В нашем случае Good - не pod, а Bad - pod тип
Для POD типов для сохранения обратной совместимости и совместимости с С Itanium C++ ABI не позволяет оптимизировать хвостовое заполнение и компилятор честно добавляет новое поле в классе S после хвостового паддинга.
Good же не pod тип, поэтому требования abi на него не распространяются и компилятор может оптимизировать размер класса и положить данные поля
c в классе S внутрь паддинга.std::cout << "Size of S<Good>: " << sizeof(S<Good>) << std::endl;
std::cout << "Size of S<Bad>: " << sizeof(S<Bad>) << std::endl;
// Size of S<Good>: 8
// Size of S<Bad>: 12
Именно поэтому размеры классов
S<Good>и S<Bad> будут 8 и 12 соответственно.У Microsoft ABI на это все дело совершенно свое мнение(как и всегда), поэтому там и размеры одинаковы.
Главное понимать, что все эти паддинги не реаламентированы стандартом по большей части и все зависит от конкретной реализации.
Behave consistently. Stay cool.
#compiler