В качестве общего практического правила (то есть делайте это, если у вас нет веской причины поступить иначе), вы хотите выровнять элементы данного типа C ++ по их выравниванию, т. Е. alignof(T)
. Если тип хочет быть выровнен по 32-битной границе (как реализовано int
в наиболее распространенной реализации С ++), он будет демонстрировать подходящее (4-байтовое) выравнивание.
Конечно, между базовых адресов двух разных объектов типа T
должно быть не менее sizeof(T)
байт пространства, что обычно будет целым числом, кратным его выравниванию (на самом деле довольно сложно передать перевыровненный тип объекту шаблонную функцию, так как она удалит любой внешний атрибут alignas
).
В большинстве случаев использования вы будете в порядке, выполнив следующие действия: Найдите первый базовый адрес в вашем базовом хранилище, который выровнен на alignof(T)
, а затем на go вперед с шагом sizeof(T)
.
Таким образом, вы будете полагаться на то, что пользователи вашего распределителя сообщат вам, чего они хотят. Это именно то, что вам нужно, поскольку оптимизатор может полагаться на знания о выравнивании и, например, выдавать выровненные по SSE нагрузки для массивов с плавающей запятой двойной точности, что приведет к ошибке sh в вашей программе, если они выровнены неправильно.
Спуск в кроличью нору
Это приводит к следующим возможным ситуациям:
- Простой шрифт, имеет длину слова и выравнивание слов (например,
int
с sizeof(int) = 4
и alignof(int) = 4
):
sizeof(T) = 4 and alignof(T) = 4
0 1 2 3 4 5 6 7 8 9 A B C D E F
[aaaaaaaaaa][bbbbbbbbbb][cccccccccc][dddddddddd]
Типы, размер которых кратен его выравниванию (например,
using T = int[2]
)
sizeof(T) = 8 and alignof(T) = 4
0 1 2 3 4 5 6 7 8 9 A B C D E F
[aaaaaaaaaaaaaaaaaaaaaa][bbbbbbbbbbbbbbbbbbbbbb]
Типы с чрезмерным выравниванием, у которых выравнивание больше, чем у размера (например,
using T = alignas(8) char[3]
).
Вот драконы! sizeof(T) = 3 and alignof(T) = 8
0 1 2 3 4 5 6 7 8 9 A B C D E F
[aaaaaaa] [bbbbbbb]
Обратите внимание, что в приведенном выше примере есть неиспользуемое пространство . Это необходимо, поскольку объекты, выровненные по 8-байтовой границе, нельзя размещать в другом месте, что может привести к потерям. Чаще всего для таких типов используются опции c, специфичные для ЦП, например, для предотвращения ложного совместного использования .
Наконец, есть немного странный случай, когда размер объектов больше, чем целое число, кратное их выравниванию (например,
using T = alignas(4) char[5];
). По сути, это всего лишь небольшое расширение предыдущего примера с избыточным числом типов:
sizeof(T) = 5 and alignof(T) = 4
0 1 2 3 4 5 6 7 8 9 A B C D E F
[aaaaaaaaaaaaa] [bbbbbbbbbbbbb]
Хотя выравнивание позволило бы разместить второй объект по базовому адресу 4
, уже существует
Собирая все эти примеры вместе, количество байтов, которое должно быть между базовыми адресами двух объектов типа T
, равно:
inline auto object_distance = sizeof(T) % alignof(T) == 0 ? sizeof(T) : sizeof(T) + (alignof(T) - sizeof(T) % alignof(T));