Сначала у меня были эти две цитаты, но теперь я думаю, что они на самом деле просто указывают, что такие вещи, как int &ref = t.mem;
, должны происходить во время жизни t
. Что и происходит в вашем примере.
12.7 пункт 1:
Для объекта с нетривиальным деструктором обращение к любому нестатическому члену или базовому классу объекта после завершения выполнения деструктора приводит к неопределенному поведению.
И пункт 3:
Для формирования указателя (или доступа к значению) прямого нестатического члена объекта obj
создание obj
должно быть начато, а его уничтожение не должно быть завершено, в противном случае вычисление значения указателя (или доступ к члену value) приводит к неопределенному поведению.
Здесь у нас есть полный объект типа T
и подобъект-член типа int
.
3.8 пункт 1:
Время жизни объекта типа T
начинается, когда:
- получается хранилище с правильным выравниванием и размером для типа
T
, и
- если объект имеет нетривиальную инициализацию, его инициализация завершена.
Время жизни объекта типа T
заканчивается, когда:
- если
T
является типом класса с нетривиальным деструктором (12.4), начинается вызов деструктора или
- память, которую занимает объект, используется повторно или освобождается.
Кстати, 3.7.3 п1:
Хранение этих сущностей [длительность автоматического хранения] длится до тех пор, пока блок, в котором они созданы, не выйдет.
И 3.7.5:
Продолжительность хранения подобъектов-членов, подобъектов базового класса и элементов массива равна продолжительности их полного объекта (1.8).
Так что не беспокойтесь о том, что компилятор освободит хранилище до exit
в этом примере.
В ненормативной заметке в 3.8p2 упоминается, что 12.6.2 описывает время жизни базовых и подобъектов-членов, но язык там говорит только об инициализации и деструкторах, а не о хранении или времени жизни, поэтому я делаю вывод, что этот раздел не влияет на определение времени жизни для подобъектов тривиального типа.
Если я правильно интерпретирую все это, когда renew
является ложным, время жизни всего объекта класса заканчивается в конце явного вызова деструктора, НО время жизни подобъекта int
продолжается до конца программы.
3.8, параграфы 5 и 6 говорят, что указатели и ссылки на выделенное хранилище до или после времени жизни любого объекта могут использоваться ограниченным образом, и перечисляют множество вещей, которые вы не можете с ними делать. Преобразование Lvalue-to-Rvalue, как и выражение ref == 42
, является одной из таких вещей, но это не проблема, если время жизни int
еще не закончилось.
Итак, я думаю, что с renew
ложью программа правильно сформирована, и assert
преуспевает!
Если renew
равно true, хранилище повторно используется программой, поэтому время жизни исходного int
заканчивается, и начинается время жизни другого int
. Но тогда мы попадаем в 3.8 пункт 7:
Если после того, как время жизни объекта закончилось и до того, как хранилище, которое занимал объект, было повторно использовано или освобождено, новый объект создается в месте хранения, которое занимал исходный объект, указатель, указывающий на исходный объект, ссылка, которая относится к исходному объекту, или имя исходного объекта будет автоматически ссылаться на новый объект и, как только начнется время жизни нового объекта, может использоваться для управления новым объектом, если:
- хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и
- новый объект имеет тот же тип, что и исходный объект (игнорируя cv-квалификаторы верхнего уровня), и
- тип исходного объекта не является константным, и, если тип класса, не содержит нестатических элементов данных, чей тип является константным или ссылочным типом, и
- исходный объект был наиболее производным объектом (1.8) типа
T
, а новый объект является наиболее производным объектом типа T
(то есть они не являются подобъектами базового класса).
Первый пункт здесь самый сложный. Для класса стандартной компоновки, такого как ваш T
, один и тот же элемент обязательно должен всегда находиться в одном и том же хранилище. Я не уверен, требуется ли это технически, если тип не является стандартным.
Хотя можно ли по-прежнему использовать ref
, в этом примере есть еще одна проблема.
12.6.2 пункт 8:
Если после завершения вызова конструктора для класса X
член класса X
не инициализируется и ему не присваивается значение во время выполнения составного оператора тела конструктора, член имеет неопределенное значение. .
Это означает, что реализация совместима, если она устанавливает t.mem
в ноль или 0xDEADBEEF
(и иногда режимы отладки действительно делают такие вещи перед вызовом конструктора).
person
aschepler
schedule
24.07.2012
&p
?p
не объявлено. - person Johannes Schaub - litb   schedule 19.08.2012