TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.37K subscribers
Post #1040 3.34K
​​WAT
#опытным

Спасибо, @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
  • 🔥 28
  • ❤ 12
  • 👍 6
  • 😁 6
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →