Существуют ли какие-либо гарантии для профсоюзов, которые содержат обернутый тип и сам тип?

Можно ли положить T и завернутый T в union и проверить их как мне нравится?

union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};
// for simplicity T = int

Example ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // ?

Стандарты С++ 11 гарантируют сохранение только обычной исходной последовательности, но value не является struct. Я полагаю, что ответ нет, так как завернутые типы даже не гарантированно совместимы с их развёрнутой копией и доступ к неактивным членам только хорошо определен в общих начальных последовательностях.

Ответ 1

Я считаю, что это поведение undefined.

[class.mem] дает нам:

Общая начальная последовательность двух типов структуры стандартного макета - это самая длинная последовательность нестатических элементов данных и бит-полей в порядке объявления, начиная с первого такого объекта в каждой из структур, так что соответствующие объекты имеют макет совместимые типы, и ни одна из них не является битовым полем, либо оба являются битовыми полями с одинаковой шириной. [...]

В объединении стандартного макета с активным членом типа struct T1 разрешено читать нестатический член данных m другого члена объединения типа struct T2, если m является частью общая начальная последовательность T1 и T2; поведение выглядит так, как если бы был назначен соответствующий член T1.

Если T не является стандартным типом структуры раскладки, это, очевидно, поведение undefined. (Обратите внимание, что int не является стандартным типом структуры компоновки, поскольку он вообще не является типом класса).

Но даже для стандартных типов структуры компоновки то, что составляет "общую начальную последовательность", основывается исключительно на нестатических элементах данных. То есть T и struct { T val; } не имеют общей исходной последовательности - вообще нет данных.

Следовательно, здесь:

template <typename T>
union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};


Example<int> ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // (*)

вы получаете доступ к неактивному члену союза. Это undefined.

Ответ 2

Поведение в профсоюзе undefined при доступе к члену, который не был последним. Нет, вы не можете зависеть от этого поведения.

В принципе это идентично идее создания объединения для извлечения определенных байтов из целого числа; но с дополнительным риском того факта, что теперь вы зависите от компилятора, не добавляя никаких дополнений в свою структуру. Подробнее см. Доступ к неактивному члену профсоюза и поведению undefined?.

Ответ 3

Он должен работать, потому что оба Example и Wrapped являются стандартными классами макета, а в стандарте С++ 14 достаточно требований, чтобы гарантировать, что в этом случае value и wrapper.wrapped расположены по тому же адресу. Проект n4296 говорит в 9.2 членах класса [class.mem] §20:

Если объект класса стандартного макета имеет нестатические члены данных, его адрес совпадает с адресом его первого нестатического элемента данных.

В заметке даже говорится:

[Примечание. Таким образом, в структуре стандартного макета может быть неназванное заполнение объект, но не в его начале, по мере необходимости, для достижения соответствующего выравнивания. -end note]

Это означает, что вы, по крайней мере, уважаете правило строгого сглаживания от 3.10 Lvalues ​​и rvalues ​​[basic.lval] §10

Если программа пытается получить доступ к сохраненному значению объекта через значение gl, отличное от одного из следующие типы: undefined
- динамический тип объекта,
...
- совокупность или тип объединения, который включает один из вышеупомянутых типов среди его элементов или нестатических члены данных (в том числе, рекурсивно, элемент или нестатический элемент данных для суммирования или содержит объединение),

Итак, это отлично определено:

cout << *(&ex.wrapper.wrapped) << endl

потому что &ex.wrapper.wrapped требуется, чтобы он был таким же, как &ex.value, и указанный объект имеет правильный тип. , Но поскольку стандарт явственен только для общей подпоследовательности. Поэтому мое понимание cout << ex.wrapper.wrapped << endl вызывает поведение undefined из-за примечания в 1.3.24 [defns.undefined] о undefined поведение говорит (подчеркивайте мое):

Undefined можно ожидать, когда этот международный стандарт исключает любое явное определениеповедение...

TL/DR: я бы поставил монету, что большинство, если не вся общая реализация ее примет, но из-за примечания 1.3.24 [defns.undefined] я никогда не использовал бы это в производственном коде, но использовал бы *(&ex.wrapper.wrapped).


В более позднем проекте n4659 для С++ 17 соответствующее понятие является взаимозаменяемостью ([basic.compound] §4).