Использование Template with Union генерирует Unknown Exception [duplicate]

Вы запускаете два разных потока, но они будут работать по-настоящему параллельно, если у вас есть хотя бы двухъядерная машина. Многопоточность Google Java.

88
задан jww 20 September 2016 в 16:11
поделиться

5 ответов

Путаница заключается в том, что C явно разрешает пул через объединение, тогда как C ++ () не имеет такого разрешения.

6.5.2.3 Элементы структуры и объединения

95) Если элемент, используемый для чтения содержимого объекта объединения, не совпадает с элементом, который использовался последним сохранить значение в объекте, соответствующая часть представления объекта значения интерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый «type punning»). Это может быть ловушечное представление.

Ситуация с C ++:

9.5. Unions [class.union]

В объединении не более одного нестатического элемента данных могут быть активны в любое время, то есть значение не более одного из нестатических члены данных могут быть сохранены в союзе в любое время.

C ++ позже имеет язык, допускающий использование объединений, содержащих struct s с общими начальными последовательностями; это, однако, не позволяет печатать по типу.

Чтобы определить, разрешен ли тип объединения в C ++, мы должны искать дальше. Напомним, что является нормативной ссылкой для C ++ 11 (и C99 имеет схожий язык с C11, разрешающим объединение типа-punning):

3.9 Типы [basic.types ]

4 - Объектное представление объекта типа T является последовательностью N неподписанных объектов char, взятых объектом типа T, где N равно sizeof (T). Представление значения объекта представляет собой набор битов, которые содержат значение типа T. Для типов с тривиальной копией представление значений представляет собой набор бит в представлении объекта, который определяет значение, которое является одним дискретным элементом реализации, определенный набор значений. 42 42) Цель состоит в том, что модель памяти C ++ совместима с моделью языка программирования ISO / IEC 9899.

Это особенно интересно, когда мы читаем

3.8 Время жизни объекта [basic.life]

Время жизни объекта типа T начинается, когда: - получено хранилище с правильным выравниванием и размером для типа T, и - если объект не имеет

Итак, для примитивного типа (который ipso facto имеет тривиальную инициализацию), содержащегося в объединении, время жизни объекта охватывает, по крайней мере, время жизни самого союза. Это позволяет нам вызывать

3.9.2 Типы соединений [basic.compound]

Если объект типа T расположен по адресу A, указатель типа cv T *, значение которого является адресом A, указывается на этот объект, независимо от того, как было получено это значение.

Предполагая, что операция, которую нас интересует, значение неактивного члена профсоюза и приведенное выше, что мы имеем действительную ссылку на объект, на который ссылается этот член, эта операция является преобразованием lvalue-to-rvalue:

4.1 Преобразование Lvalue-to-rvalue [conv.lval]

Значение gl для нефункционного типа без массива T может быть преобразовано в prvalue. Если T является неполным типом, программа, которая требует этого преобразования, плохо сформирована. Если объект, к которому относится glvalue, не является объектом типа T и не является объектом типа, полученного из T, или если объект не инициализирован, программа, которая требует этого преобразования, имеет неопределенное поведение.

Тогда возникает вопрос, инициализируется ли объект, который является неактивным членом профсоюза, хранилищем активному члену объединения. Насколько я могу судить, это не так, поэтому, если:

  • объединение копируется в хранилище массива char и обратно (3.9: 2) или
  • объединение копируется в другое объединение одного и того же типа (3.9: 3) или
  • соединение осуществляется через границы языка программным элементом, соответствующим ISO / IEC 9899 (поскольку (3.9: 4 примечание 42), затем

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

То есть, хотя мы можем законно сформировать lvalue для не- активный член объединения (поэтому назначение неактивного элемента без построения в порядке) считается неинициализированным.

102
ответ дан Ben Voigt 16 August 2018 в 05:32
поделиться
  • 1
    @mpu он должен присутствовать; ищите 6.5.2.3, сноска 82. – ecatmur 17 August 2012 в 11:06
  • 2
    3.8 / 1 говорит, что срок жизни объекта заканчивается, когда его хранилище повторно используется. Это указывает на то, что неактивный член времени объединения закончился, потому что его хранилище было повторно использовано для активного члена. Это будет означать, что вы ограничены тем, как вы используете участника (3.8 / 6). – bames53 18 October 2012 в 20:35
  • 3
    @ bames53 хорошая точка, но если она имеет тривиальную инициализацию, то ее время жизни начинается снова сразу или при обращении к неактивному члену ( получается хранилище с правильным выравниванием и размером для типа T ). – ecatmur 19 October 2012 в 09:05
  • 4
    В соответствии с этой интерпретацией каждый бит памяти одновременно содержит объекты всех типов, которые тривиально инициализируются и имеют соответствующее выравнивание ... Таким образом, время жизни любого нетривиально-инициализируемого типа немедленно заканчивается, поскольку его хранилище повторно используется для всех этих других типов ( и не перезапускать, потому что они не тривиально инициализируются)? – bames53 19 October 2012 в 15:08
  • 5
    Вы можете найти некоторые из ссылок, которые я связываю в с этим ответом , на интересные темы. Особенно цитата Паскаля Куока в моей сноске. Также возникает вопрос, так как вы ссылаетесь на C99 как нормативную ссылку для C ++ 11, у вас есть позиция в Можем ли мы применить контент, явно не цитируемый из нормативных ссылок на стандарт C ++? ? – Shafik Yaghmour 25 June 2014 в 13:26

Стандарт C ++ 11 говорит об этом следующим образом

9.5 Unions

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

Если хранится только одно значение, как вы можете читать еще? Это просто не существует.


Документация gcc перечисляет это в . Определенное поведение реализации

  • A член объекта union обращается с использованием члена другого типа (C90 6.3.2.3).

Соответствующие байты представления объекта рассматриваются как объект типа, используемого для доступа. См. «Тип-караун». Это может быть ловушечное представление.

, указывающее, что это не требуется стандартом C.


2016-01-05: через комментарии я был связанный с Отчет о дефекте C99 № 283 , который добавляет аналогичный текст в виде сноски к стандартным документам C:

78a) Если элемент используется для доступа к содержимому union - это не то же самое, что последний элемент, используемый для хранения значения в объекте, соответствующая часть представления объекта значения интерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый «type punning»). Это может быть ловушечное представление.

Не уверен, что он много разъясняет, учитывая, что сноска не является нормативной для стандарта.

22
ответ дан Bo Persson 16 August 2018 в 05:32
поделиться
  • 1
    @LuchianGrigore: UB не соответствует стандарту UB, вместо этого он не описывает, как он должен работать. Это именно такой случай. Означает ли стандарт, что происходит? Говорит ли он, что это реализация определена? Нет и нет. Так что это UB. Кроме того, что касается «членов», имеющих один и тот же адрес памяти », аргумент, вам придется обратиться к правилам псевдонимов, которые снова приведут вас к UB. – ybungalobill 7 July 2012 в 08:52
  • 2
    @Luchian: Совершенно ясно, что означает активное средство ", то есть значение не более одного из нестатических элементов данных может быть сохранено в союзе в любое время. & Quot; – Benjamin Lindley 7 July 2012 в 08:55
  • 3
    @LuchianGrigore: Да, есть. Существует бесконечное количество случаев, когда стандарт не адресуется (и не может). (C ++ - полная виртуальная машина Тьюринга, так что она неполна.) Итак, что? Он объясняет, что такое "активный" означает ссылку на приведенную выше цитату после «, которая равна». – ybungalobill 7 July 2012 в 08:55
  • 4
    @LuchianGrigore: Отсутствие явного определения поведения также не учитывает неопределенное поведение, согласно разделу определений. – jxh 7 July 2012 в 08:59
  • 5
    @Claudiu Это UB по другой причине - это нарушает строгий псевдоним. – Mysticial 7 July 2012 в 17:27

Я думаю, что самый близкий стандарт приходит к утверждению, что неопределенное поведение - это то, где он определяет поведение для объединения, содержащего общую начальную последовательность (C99, §6.5.2.3 / 5):

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

C ++ 11 дает аналогичные требования / разрешения в §9.2 / 19:

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

Хотя ни один из них не является прямым, оба они имеют сильное значение, что «проверка» (чтение) члена «разрешена» только , если 1) она (часть) член, который недавно был написан, или 2) является частью общей исходной последовательности.

Это не прямой оператор, который делает иначе, это неопределенное поведение, но это самый близкий из которых я знаю.

16
ответ дан Jerry Coffin 16 August 2018 в 05:32
поделиться
  • 1
    Чтобы сделать это полностью, вам нужно знать, что такое «совместимые с макетами типы». для C ++ или "совместимых типов" для C. – Michael Anderson 15 August 2012 в 09:32
  • 2
    @MichaelAnderson: Да и нет. Вам нужно иметь дело с теми, когда / если вы хотите убедиться, что что-то входит в это исключение, - но реальный вопрос здесь заключается в том, действительно ли что-то явно выходит за пределы исключения, дает UB. Я думаю, что это достаточно ясно подразумевается здесь, чтобы сделать намерение понятным, но я не думаю, что это когда-либо прямо сказано. – Jerry Coffin 15 August 2012 в 16:43
  • 3
    Эта "обычная начальная последовательность" вещь могла бы просто сохранить 2 или 3 моих проектов из Rewrite Bin. Я был в ярости, когда впервые прочитал о том, что большинство пренебрегающих видов использования union не определено, так как у меня создалось впечатление от конкретного блога, что все в порядке, и построил вокруг него несколько крупных структур и проектов. Теперь я думаю , возможно, все в порядке, так как мои union s содержат классы с одинаковыми типами на передней панели – underscore_d 31 December 2015 в 14:55
  • 4
    @JerryCoffin, я думаю, вы намекали на тот же вопрос, что и я: что, если наш union содержит eg a uint8_t и class Something { uint8_t myByte; [...] }; - я бы предположил, что это условие также применимо и здесь, но он сформулирован очень преднамеренно, чтобы разрешить только struct s. К счастью, я уже использую эти вместо исходных примитивов: O – underscore_d 31 December 2015 в 15:04
  • 5
    @underscore_d: стандарт C, по крайней мере, тип обложек, который задает вопрос: «Указатель на структурный объект, соответствующим образом преобразованный, указывает на его начальный член (или если этот элемент является битовым полем, а затем в единицу, в которой он находится ) и наоборот. & quot; – Jerry Coffin 31 December 2015 в 17:00

Я хорошо объясню это на примере. предположим, что мы имеем следующий союз:

union A{
   int x;
   short y[2];
};

Я полагаю, что sizeof(int) дает 4, а sizeof(short) дает 2. когда вы пишете union A a = {10}, чтобы создать новый var типа A вставьте в него значение 10.

ваша память должна выглядеть так: (помните, что все члены союза получают одно и то же местоположение)

       |                   x                   |
       |        y[0]       |       y[1]        |
       -----------------------------------------
   a-> |0000 0000|0000 0000|0000 0000|0000 1010|
       -----------------------------------------

, как вы могли видеть, значение ax равно 10, значение ay 1 равно 10, а значение ay [0] равно 0.

теперь, что хорошо, если я это сделаю?

a.y[0] = 37;

наша память будет выглядеть так:

       |                   x                   |
       |        y[0]       |       y[1]        |
       -----------------------------------------
   a-> |0000 0000|0010 0101|0000 0000|0000 1010|
       -----------------------------------------

это превратит значение ax в 2424842 (в десятичном виде). ​​

теперь, если ваш союз имеет float или double, ваша карта памяти будет скорее беспорядочной, потому что вы храните точные числа. более подробную информацию вы можете получить в здесь .

-2
ответ дан Marco A. 16 August 2018 в 05:32
поделиться
  • 1
    :) Это не то, что я спросил. Я знаю, что происходит внутри страны. Я знаю, что это работает. Я спросил, соответствует ли это стандарту. – Luchian Grigore 17 August 2012 в 08:08
10
ответ дан mpu 16 August 2018 в 05:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: