Подтвердить что ты не робот

Являются ли члены типа POD-структуры или стандартного типа макета гарантированным выравниванием в соответствии с их требованиями к выравниванию?

Учитывая POD-struct (в С++ 03) или стандартный тип макета (в С++ 11), со всеми членами, имеющими фундаментальное требование выравнивания, верно ли, что каждый член гарантированно выравнивается в соответствии с его требования к выравниванию?

Другими словами, для всех членов m_k в {m0... mn} стандартного типа макета S,

  struct S {
    T0 m0;
    T1 m1;
    ...
    TN mn;
  };

является следующим выражением, гарантируемым для оценки true?

  (offsetof(S,m_k) % alignof(decltype(S::m_k))) == 0

Просьба дать ответы как на С++ 03, так и на С++ 11 и привести соответствующие части стандарта. Полезным будет также поддержка доказательств из стандартов C.


Мое чтение стандарта С++ 03 (ISO/IEC 14882: 2003 (E)) заключается в том, что он молчал относительно выравнивания членов внутри POD-структуры, за исключением первого члена. Соответствующие пункты:

В языке спецификации объект является "областью хранения":

1.8 Объектная модель C + + [intro.object]

1.8/1 Конструкции в программе C + + создают, уничтожают, ссылаются, получают доступ и манипулируют объектами. Объект - это область хранения....

Объекты распределяются в соответствии с их требованиями к выравниванию:

3.9 Типы [basic.types]

3.9/5 Типы объектов имеют требования к выравниванию (3.9.1, 3.9.2). Выравнивание полного типа объекта представляет собой целочисленное значение, определенное реализацией, представляющее собой количество байтов; объект выделяется по адресу, соответствующему требованиям к выравниванию его типа объекта.

Фундаментальные типы имеют требования к выравниванию:

3.9.1 Основные типы [basic.fundamental]

3.9.1/3 Для каждого из знаковых целочисленных типов существует соответствующий (но другой) целочисленный тип без знака: "unsigned char", "unsigned short int", "unsigned int" и "unsigned long" int ", каждый из которых занимает один и тот же объем памяти и имеет те же требования к выравниванию (3.9), что и соответствующий тип целочисленного знака;...

Заполнение может происходить из-за "требований к выравниванию реализации":

9.2 Члены класса [class.mem]

9.2/12 Нестационарные члены данных (неединичного) класса, объявленные без промежуточного спецификатора доступа, распределяются таким образом, что более поздние члены имеют более высокие адреса в объекте класса. Порядок распределения нестатических члены данных, разделенные спецификатором доступа, не определены (11.1). Требования к выравниванию реализации могут привести к тому, что два соседних элемента не будут распределены сразу после друг друга; поэтому могут потребоваться пространство для управления виртуальными функциями (10.3) и виртуальными базовыми классами (10.1).

Имеет ли слово "выделение" в 9.2/12 то же значение, что и "выделено" в 3.9/5? Большинство применений "выделенных" в спецификации относятся к распределению динамического хранилища, а не к структурно-внутренней компоновке. Использование возможности в 9.2/12, по-видимому, подразумевает, что требования к выравниванию 3.9/5 и 3.9.1/3 могут быть строго не требуются для членов структуры.

Первый член POD-структуры будет выровнен в соответствии с требованием выравнивания структуры:

9.2/17 Указатель на объект POD-структуры, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный член (или если этот элемент является битовым полем, а затем блоком, в котором он находится) и наоборот. [ Примечание. Таким образом, для создания подходящего выравнивания может возникнуть неназванное заполнение внутри объекта POD-структуры, но не в его начале.]

[Акцент добавлен во всех приведенных выше цитатах.]

4b9b3361

Ответ 1

Каждый элемент структуры POD сам является объектом, а объекты могут быть распределены только в соответствии с требованиями к выравниванию для этих объектов. Однако требования к выравниванию могут измениться из-за того, что что-то является под-объектом другого объекта ([basic.align]/1, 2:

1 Типы объектов имеют требования к выравниванию (3.9.1, 3.9.2), которые устанавливают ограничения по адресам, на которых может быть выделен объект такого типа. Выравнивание представляет собой целочисленное значение, определяемое реализацией, представляющее количество байтов между последовательными адресами, по которым может быть выделен данный объект. Тип объекта налагает требование выравнивания для каждого объекта этого типа; более строгое выравнивание можно запросить с помощью спецификатора выравнивания (7.6.2).

2 Фундаментальное выравнивание представлено выравниванием, которое меньше или равно наибольшему выравниванию, поддерживаемому реализацией во всех контекстах, которое равно alignof(std::max_align_t) (18.2). Выравнивание, требуемое для типа, может отличаться, когда оно используется как тип полного объекта и когда оно используется как тип подобъекта. [Пример:

struct B { long double d; };
struct D : virtual B { char c; }

Когда D является типом полного объекта, он будет иметь подобъект типа B, поэтому он должен быть соответствующим образом выровнен для a long double. Если D появляется как подобъект другого объекта, который также имеет B в качестве виртуального базового класса, Subobject B может быть частью другого подобъекта, уменьшая требования к выравниванию в примере subbject. . Результат оператора alignof отражает требование выравнивания типа в полный объект.

[выделено курсивом]

Хотя примеры относятся к под-объекту через наследование, нормативная формулировка просто относится к под-объектам в целом, поэтому я считаю, что применяются одни и те же правила, поэтому с одной стороны вы можете предположить, что каждый под-объект выровнен так, что к нему можно получить доступ. С другой стороны, нет, вы не можете предположить, что это будет то же самое выравнивание, которое дает alignof.

[Ссылка из N4296, но я считаю, что то же самое относится ко всем последним версиям. У С++ 98/03, конечно, вообще не было alignof, но я считаю, что применяется тот же базовый принцип - члены будут выровнены, чтобы их можно было использовать, но это требование выравнивания не обязательно одинаково как когда они используются как независимые объекты.]

Ответ 2

Для каждого типа есть два различных требования к выравниванию. Один соответствует полным объектам, другой - к подобъектам. [Basic.align]/2:

Выравнивание, требуемое для типа, может отличаться при использовании как тип полного объекта и когда он используется как тип подобъект. [Пример:

struct B { long double d; };
struct D : virtual B { char c; };

Когда D является типом полного объекта, он будет иметь подобъект введите B, поэтому он должен быть соответствующим образом выровнен для long double. Если D появляется как подобъект другого объекта, который также имеет B как виртуальный базовый класс, подобъект B может быть частью другого подобъектом, уменьшая требования к выравниванию в подобъекте D. -end example] Результат оператора alignof отражает требование выравнивания типа в случае полного объекта.

Я не могу придумать какой-либо конкретный пример для субобъектов-членов, и, безусловно, нет! -, но вышеприведенный абзац упускает возможность того, что подобъект-член более слаб, чем alignof, что приведет к поломке вашего состояния.