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

Reinterpret_cast, создающий тривиальный конструктивный объект по умолчанию

cppreference & dagger; заявляет, что:

Объекты с тривиальными конструкторами по умолчанию могут быть созданы с помощью reinterpret_cast на любом подходящем выровненном хранилище, например. на память, выделенную с помощью std::malloc.

Это означает, что следующий код является корректным:

struct X { int x; };
alignas(X) char buffer[sizeof(X)];    // (A)
reinterpret_cast<X*>(buffer)->x = 42; // (B)

Следующие три вопроса:

  • Правильно ли это цитата?
  • Если да, в какой момент начинается время жизни X? Если в строке (B), является ли это литой, которая считается приобретением памяти? Если в строке (A), если бы существовала ветвь между (A) и (B), которая условно построила X или какой-либо другой модуль, Y?
  • Что-то меняется между С++ 11 и С++ 1z в этом отношении?

& dagger; Обратите внимание, что это старая ссылка. Формулировка была изменена в ответ на этот вопрос. Теперь он гласит:

Однако, в отличие от C, объекты с тривиальными конструкторами по умолчанию не могут быть созданы путем простого переинтерпретации подходящего выравнивания хранилища, такого как память, выделенная с помощью std::malloc: place-new требуется для формального введения нового объекта и предотвращения потенциальных undefined поведение.

4b9b3361

Ответ 1

Нет объекта X, живого или иного, поэтому притворяясь, что есть один результат в поведении undefined.

[intro.object]/1 излагает исчерпывающе при создании объектов:

Объект создается определением ([basic.def]), посредством new-expression ([expr.new]), когда неявное изменение активного член союза ([class.union]), или когда временный объект ([conv.rval], [class.temporary]).

С принятием P0137R1 этот параграф является определением термина "объект".

Существует ли определение объекта X? Нет. Есть ли новое выражение? Нет. Есть союз? Нет. Есть ли в вашем языке языковая конструкция, которая создает временный объект X? Нет.

Независимо от того, что [basic.life] говорит о времени жизни объекта с пустой инициализацией, не имеет значения. Для этого вам необходимо иметь объект в первую очередь. Вы этого не делаете.

С++ 11 имеет примерно тот же абзац, но не использует его как определение "объект". Тем не менее, толкование остается тем же. Альтернативная интерпретация - обработка [basic.life] как создания объекта, как только подходящее хранилище - означает, что вы создаете объекты Шредингера * что противоречит N3337 [intro.object]/6:

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


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

Ответ 2

Этот анализ основан на n4567 и использует номера разделов из него.

§5.2.10/7: Когда prvalue v типа указателя объекта преобразуется в тип указателя объекта "указатель на cv T", результатом является static_cast<cv T*>(static_cast<cv void*>(v)).

Итак, в этом случае reinterpret_cast<X*>(buffer) совпадает с static_cast<X *>(static_cast<void *>(buffer)). Это приводит нас к рассмотрению соответствующих частей о static_cast:

§5.2.9/13: prvalue типа "указатель на cv1 void" может быть преобразовано в prvalue типа "указатель на cv2 T", где T - тип объекта, а cv2 - это та же самая cv-квалификация, что и, или больше cv-квалификации, чем cv1. Значение нулевого указателя преобразуется в значение нулевого указателя для типа назначения. Если исходное значение указателя представляет адрес A байта в памяти, а A удовлетворяет требованию выравнивания T, то полученное значение указателя представляет тот же адрес, что и исходное значение указателя, то есть A.

Я считаю, что достаточно сказать, что исходная цитата является правильной - это преобразование дает определенные результаты.

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

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