Предположим, такие структуры: A находятся вне нашего контроля, но мы можем изменить структуру B:
struct A {
int64_t x; // 8 bytes
int32_t y; // 4 bytes
// 4 padding bytes
};
struct B {
A a; // 16 bytes
int32_t z; // 4 bytes
// 4 padding bytes (alignof(B) == alignof(A) == 8)
};
static_assert(sizeof(B) == 24); // 8 useless bytes!
Есть ли способ поместить z внутрь A отступа (некоторые атрибуты)?
В некоторых компиляторах есть packpragma, но будьте очень осторожны с его использованием. Эту структуру больше нельзя будет использовать в системах со строгими требованиями к выравниванию, и, вероятно, она будет работать медленнее в тех, которые этого не делают.
Вы можете создать A шаблон класса, чтобы он мог хранить любое количество дополнительной информации, например, в tuple.
@TedLyngmo о A мы ничего не знаем - только то, что у него могут быть байты заполнения
@AlexanderS Хорошо, тогда я бы оставил эти байты заполнения. Интересно, что вы знаете, что они у него есть. Компилятор предупреждает об этом?
Я не получаю никаких предупреждений, но потеря 8 байт в этом случае заставляет меня плакать
Тогда что делает A? Напишите свой A, где сможете максимально плотно упаковать всю информацию.
Это скорее расследование (опять :)). B хранит A и какой-то примитивный тип, который мал по сравнению с A, и мне бы хотелось избежать сильного расширения B (что, если alignof(A) == 16?)
@AlexanderS Ну, ради любопытства, у пользователя 4581301 есть ответ - у большинства компиляторов есть __attribute__, #pragma или любой другой специфичный для компилятора способ запретить отступы. Очевидно, что он непереносим, поскольку каждый компилятор имеет свой собственный синтаксис для его написания, и, как уже упоминалось, он может нарушить требования к псевдонимам или, по крайней мере, замедлить доступ к структуре.
Используете ли вы классы стандартной библиотеки? Если да, то в них вы также можете найти 2- и 4-байтовые дополнения. Шаблоны классов, такие как std::vector, могут быть заполнены внутренними отступами, и наличие большого количества vector в приложении не является чем-то необычным.
@Yksisarvinen Согласен, наверное, оно того не стоит, если мы хотим сохранить хорошую производительность.
@TedLyngmo всегда) Ух ты, я думал, что контейнеры STL очень хорошо спроектированы (без отступов и т. д.).
Взгляните на себя. Те, на которые я смотрел, хорошо спроектированы, а у некоторых есть отступы.
@AlexanderS Я бы не стал рассматривать «отсутствие отступов» как характеристику хорошего дизайна. Если у вас есть система, в которой несколько байтов заполнения являются проблемой, вы все равно не будете использовать стандартные контейнеры (возможно, с баром std::array). Стандартные контейнеры в основном созданы для скорости и удобства использования. Единственный контейнер, рассчитанный на размер, — std::forward_list, и я никогда не видел, чтобы кто-нибудь им пользовался. Серьезно, я думаю, что ты фокусируешься не на том. Компьютеры имеют гигабайты оперативной памяти, встроенные системы — мегабайты. «Потратить» часть этого на скорость всегда лучше, если только вы действительно не оказались в ограниченной системе.
Они хорошо спроектированы, но если содержимое нуждается в дополнении, оно должно быть дополнено. А поскольку такое заполнение обычно приводит к более высокой скорости, скажем так, обмен оперативной памяти на скорость — это проверенная временем традиция программирования.





Повторное использование заполнения определяется ABI, а не стандартом или компилятором. Например, в Linux это будет работать:
struct A {
int64_t x; // 8 bytes
int32_t y; // 4 bytes
// 4 padding bytes
A() = default; // <-- CHANGE HERE
};
struct B {
[[no_unique_address]] A a; // <-- CHANGE HERE
int32_t z; // 4 bytes in the padding of A
};
static_assert(sizeof(B) == 16);
A необходимы определенные свойства, соответствующие его типу, чтобы иметь право на повторное использование хвостовой части. Для этого ABI он не должен быть POD, как в C++03. Достаточно добавить объявленный пользователем конструктор.
Затем в B нам нужно заставить использовать отступы хвоста. Например, если A является базовым классом, он будет повторно использовать свое хвостовое дополнение. Альтернативно, использование [[no_unique_address]] дает тот же результат.
Я понимаю общую идею такого поведения, но что, если вы хотите добиться другого?