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 поведение.
Ответы
Ответ 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
, и имеет точно такое же время жизни, независимо от того, создаете ли вы указатель (того же или преобразованного типа) для этого хранилища или нет.