Общая начальная последовательность и выравнивание

Обдумывая контрпример для этого вопроса, я придумал:

struct A
{
    alignas(2) char byte;
};

Но если это законный и стандартный макет, совместим ли он с этим struct B?

struct B
{
    char byte;
};

Кроме того, если у нас есть

struct A
{
    alignas(2) char x;
    alignas(4) char y;
};
// possible alignment, - is padding
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
//  x  -  -  -  y  -  -  -  x  -  -  -  y  -  -  -

struct B
{
    char x;
    char y;
}; // no padding required

union U
{
    A a;
    B b;
} u;

Есть ли общая начальная последовательность для A и B? Если да, включает ли он A::y & B::y? То есть, можем ли мы написать следующее без вызова UB?

u.a.y = 42;
std::cout << u.b.y;

(также приветствуются ответы на C ++ 1y / "исправленный C ++ 11")


  • См. [Basic.align] для выравнивания и [dcl.align] для спецификатора выравнивания.

  • [basic.types] / 11 говорит о базовых типах: «Если два типа T1 и T2 относятся к одному и тому же типу, то T1 и T2 являются типами, совместимыми с макетом». (основной вопрос заключается в том, имеют ли A::byte и B::byte типы, совместимые с макетом)

  • [class.mem] / 16 «Два типа структур со стандартным макетом совместимы с макетом, если они имеют одинаковое количество нестатических элементов данных, а соответствующие нестатические элементы данных (в порядке объявления) имеют типы, совместимые с макетом».

  • [class.mem] / 18 "Две структуры стандартной компоновки имеют общую начальную последовательность, если соответствующие члены имеют типы, совместимые с компоновкой, и либо ни один из них не является битовым полем, либо оба являются битовыми полями с одинаковой шириной для последовательности из одного или более начальных членов ".

  • [class.mem] / 18 "Если объединение стандартного макета содержит две или более структур стандартного макета, которые имеют общую начальную последовательность, и если объект объединения стандартного макета в настоящее время содержит одну из этих структур стандартного макета, это разрешено осмотреть общую начальную часть любого из них ».

Конечно, на уровне языкового юриста другой вопрос заключается в том, что означает «разрешенная» проверка общей исходной последовательности. Я предполагаю, что какой-то другой абзац может вызвать указанное выше u.b.x неопределенное поведение (чтение из неинициализированного объекта).


person dyp    schedule 01.02.2014    source источник
comment
Не думаю, что это хороший пример. Структура с int и char имеет выравнивание int. Атрибут alignas(2) для char byte в качестве первого элемента не работает, потому что этот первый элемент уже имеет alignas(int) выравнивание. Возможно, лучший пример: struct A {int x; alignas(double) char byte;};   -  person David Hammen    schedule 01.02.2014
comment
@DavidHammen Ой, правда, я хотел добавить отступ после байта. Исправление ..   -  person dyp    schedule 01.02.2014
comment
@DavidHammen Надеюсь, теперь пример лучше.   -  person dyp    schedule 01.02.2014
comment
Перед x нет отступов (относительно ASCII-art, где x находится на 02)   -  person    schedule 02.02.2014
comment
@ DieterLücking Хм, это было бы незаконно. В начале структуры стандартного макета не может быть отступов. Но Стандарт не допускает и нечетного выравнивания, которое я имел в виду, поэтому я удалил эту строку. Остающийся - это выравнивание, которое, похоже, используют g ++ и clang ++.   -  person dyp    schedule 02.02.2014
comment
Хм. Я думал, что получил ответ, но потом подумал еще немного. Чем больше я смотрю на стандарт, тем больше возникает проблема рассогласования. alignas является частью type-id или нет? В некоторых местах кажется, что это так, в других кажется, что это определенно не так.   -  person David Hammen    schedule 02.02.2014
comment
@DavidHammen Да .. Я начал интересоваться этой проблемой, когда попробовал static_assert(std::is_same<decltype(A::byte), char>::value, "!");, который затем приводят к этому вопросу.   -  person dyp    schedule 02.02.2014
comment
Боковое примечание: если класс, использующий alignas на своих элементах, не предназначен для использования в стандартной компоновке, тогда sizeof(A) может быть равно четырем, причем второй член находится со смещением 0, а первый - со смещением 2. Несколько более уместное примечание: текущая формулировка стандартный макет, который уже делает буквальные требования невыполнимыми по другим причинам. Подробности здесь. Я искал открытые проблемы по поводу выравнивания тоже, но ничего интересного не нашел.   -  person    schedule 23.02.2014
comment
Противный. Но да, похоже, что Стандарт не справляется с этим достаточно хорошо. Очевидное намерение состоит в том, чтобы определить совместимые с макетом структуры и общие начальные последовательности, включающие одни и те же базовые классы с одинаковыми выравниваниями и одни и те же типы членов с одинаковыми выравниваниями.   -  person aschepler    schedule 03.05.2014
comment
@Tshepang Согласно тегу wiki, [union] предназначен для SQL UNION, тогда как [unions] для C, C ++ и т. Д. unions.   -  person dyp    schedule 03.05.2014
comment
Это кажется вынужденным @dyp. Нам нужны более качественные теги, возможно, c-union.   -  person tshepang    schedule 03.05.2014
comment
@Tshepang Я согласен. Возможно, [union] следует заменить на [SQL-UNION], а [union] - на [c-union]. Может быть, было какое-то обсуждение меты? В противном случае, возможно, стоит задать новый вопрос. Изменить: только что проголосовали за ваше предложение :)   -  person dyp    schedule 03.05.2014


Ответы (3)


Похоже дырка в стандарте. Ответственным будет подать отчет о дефектах.

Однако несколько вещей:

  • Ваш первый пример на самом деле не демонстрирует проблемы. Добавление short после char также приведет к выравниванию char по 2-байтовой границе без изменения общей подпоследовательности.
  • alignas - это не только C ++; он был добавлен одновременно в C11. Поскольку свойство стандартного макета является средством межъязыковой совместимости, вероятно, предпочтительнее требовать соответствия соответствующих спецификаторов выравнивания, чем дисквалифицировать класс с нестатическим спецификатором выравнивания членов.
  • Не было бы проблем, если бы спецификаторы выравнивания элементов принадлежали типам элементов. Другие проблемы могут возникать из-за отсутствия настройки типов, например, может потребоваться изменить параметр функции ret fn( alignas(4) char ), чтобы ABI обработал его правильно, но язык может не обеспечивать такую ​​настройку.
person Potatoswatter    schedule 27.07.2014
comment
Ой, конечно. Первый пример - это более лаконичная формулировка проблемы, которая проявляется во втором примере. - person dyp; 27.07.2014

Я не могу говорить о стандарте C ++ 11, но я программист прошивок / микрочипов и должен использовать такие давно существующие функции (пакет прагм, атрибуты выравнивания).

Использование alignas не может считаться "стандартной компоновкой", поэтому все последствия бесполезны. Стандартный макет означает одно фиксированное распределение выравнивания (для каждой архитектуры - обычно все align(min(sizeof,4)) или некоторые могут быть align(8)). Стандарт, вероятно, хочет сказать очевидное: без использования специальных функций (_4 _, _ 5_) структуры совместимы с одной и той же архитектурой, если они кажутся одинаковыми (одинаковые типы в одном порядке). В противном случае они могут быть, а могут и не быть совместимыми - в зависимости от архитектуры (могут быть совместимы с одной архитектурой, но разными - с другой).

Рассмотрим эту структуру:

struct foo{ char b; short h; double d; int i; };

На одной архитектуре (например, x86 32bit) это то, чем кажется, но на Itanium или ARM это выглядит так:

struct foo{char b, **_hidden_b**; short h; **int _maybe_hidden_h**; double d; int i;}  

Обратите внимание на _maybe_hidden_h - его можно опустить в старом AEABI (выровнять максимум до 4) или там для выравнивания 64 бит / 8B.

Стандартный макет x86 (упаковка (1)):

alignas(1) char b; alignas(1) short h; alignas(1) double d; alignas(1) int i;  

32-битный стандартный макет выравнивания (пакет (4) - архитектура ARM, более старая версия - EABI)

alignas(1) char b; alignas(2) short h; **alignas(4) double d**; alignas(4) int i;  

Стандартный макет 64-битного выравнивания (упаковка (8) - Itanium и новее ARM / AEABI)

alignas(1) char b; alignas(2) short h; **alignas(8) double d**; alignas(4) int i;

В вашем примере:
offsetof(A,y) = 4 в то время как offsetof(B,y) = 2 и объединение не меняет этого (таким образом, &u.a.y != u.b.y)

person firda    schedule 26.07.2014

(основной вопрос заключается в том, имеют ли A :: byte и B :: byte типы, совместимые с макетом)

да. Это важная часть. Атрибут alignas принадлежит объявленному объекту, а не типу. Может быть легко протестирован std::is_same и decltype.

То есть, можем ли мы написать следующее без вызова UB?

Следовательно, это не UB, соответствующие абзацы были процитированы вами.

РЕДАКТИРОВАТЬ: Простите меня, это, конечно, может привести к UB, потому что заполнение между членами не (или реализация) не определено (§9.2 / 13)! Я случайно неправильно прочитал пример, потому что я думал, что он обращается к x вместо y, потому что с x он фактически всегда работает - тогда как с y теоретически не обязательно (хотя практически всегда будет).

person Community    schedule 02.05.2014
comment
Как же тогда это реализовано? u.a.y = 42; записывает во второй байт структуры; если u.b.y должен содержать то же значение, нужно либо отслеживать активный член объединения, либо также записывать в пятый байт структуры в u.a.y = 42;, верно? - person dyp; 02.05.2014
comment
О, подождите минутку - я только что понял, что вы сделали совершенно другой пример, чем я думал. Вы используете y вместо x. Что ж, теперь это НЕ определено, так как между членами может быть любое заполнение! Я тоже добавлю это в свой пост :) - person ; 02.05.2014
comment
Да, отступ между элементами определяется реализацией. Но если есть общая начальная последовательность, вы можете получить доступ ко всем этим членам этой последовательности (а не только к первому). Теперь возникает вопрос: какова общая начальная последовательность для этих двух структур? Включает ли он второго члена? Я бы сказал нет, но где это указано? - person dyp; 02.05.2014
comment
Вы сами дали необходимую цитату: если у членов есть типы, совместимые с макетом. А поскольку типы одинаковы, вы можете их изменять. Выравнивание автоматически берется из строжайших требований всех членов профсоюзов. Тем не менее я считаю, что это странно, так как это сделало бы предположения о совместимости структур ... - person ; 03.05.2014
comment
Значит, вы имеете в виду, что u.a.y также будет иметь выравнивание 4, поскольку u.a находится в том же объединении, что и u.b? Затем пришлось изменить другие назначения, такие как A x; x = u.a;. Я еще не уверен, как все это должно сочетаться. - person dyp; 03.05.2014
comment
Да, у него будет такое же выравнивание. В противном случае это правило не могло бы применяться. Или стандарт как-то недостаточно точно определяет, как совместимость макета связана с выравниванием объектов, объявленных с помощью alignas. - person ; 03.05.2014
comment
Я подозреваю, что дело обстоит именно так;) Вот почему я продолжаю спрашивать .. Ну, может, мне стоит спросить об этом в списке рассылки isocpp, чтобы узнать, считается ли это дефектом или нет. - person dyp; 03.05.2014