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

При наличии POD-структуры (в C++03) или стандартного типа макета (в C++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

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


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

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

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

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

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

3.9 Типы [базовые.типы]

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

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

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

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

2 ответа

Решение

Каждый элемент структуры 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, поэтому он должен быть выровнен соответствующим образом для long double, Если D появляется как подобъект другого объекта, который также имеет B как виртуальный базовый класс, B подобъект может быть частью другого подобъекта, что снижает требования к D подобъект.— конец примера ] Результат alignof Оператор отражает требование выравнивания типа в случае полного объекта.

[выделение добавлено]

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

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

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

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

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

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

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

Другие вопросы по тегам