Комментарии 2
Как вы думаете, объект какого типа неявно создастся?:
Неясно что вы называете "создаться". Формально оно уже создано, вопрос только как вы будете интерпретировать созданный объект. Вы можете хоть десять раз подряд кастить типы между собой и менять в соответсвии с полями - пока вы в пределах выбранной памяти можно писать и переписывать любые данные.
Конструирование/деструкция же типа это просто (де)инициализация полей в определённом порядке и ассоциирование некоторой логики после инициализации полей.
Так ваш первый "недостаточный" пример Foo выглядел бы так.
struct Foo{
std::string name;
/* прочие поля */
Foo();
};
Foo::Foo()
: name("foo")
{}Единственная разница - при нехватке памяти malloc вернёт nullptr, а new Foo выкинет исключение bad_alloc. Ну и если мы хотим явно привязать какую-то логику после конструирования, то мы её просто инкапсулируем в пределах конструктора, в то время как в Си это делается посредством джентельменского соглашения или заворачивание этой логики в какой-нибудь foo_new() и foo_free(), что суть то же соглашение.
Обращение к неинициализированным полям в обоих ситуациях остаётся UB.
magic,version,flags- таким кодом тоже будут неявно созданы
опять же - нет. созданы они строчкой выше, на моменте где вы добыли себе байты. Объявление лайфтайма фактически тот же reinterpreter_cast, но семантически чище. Инициализации типов или выделений памяти на этом этапе не происходит. Отсюда и проблема с инициализациие лайфтаймов подобъектов.
Что делать? Я знаю только одну опцию:
ничего не делать, т.к. формально состояние созданного объекта уже ill-former уже после объявление начала времени жизни и дальнейшии манипуляции с ним становятся UB. Поэтому s->i = 3; тоже будет UB, т.к. необходимый инвариант уже нарушен. Если вы уверены в поведении вашего компилятора, то с этим в теории можно работать, но без гарантий, как и с любым UB.
К
t1мы нормально обращаться не можем, поэтому создаем черезstd::launderновый указательt2с provenance, отсылающим к объектуw->t
картинка врёт. t2 после std::lauder не будет иметь отдельного объекта и должно ссылаться на начало Wrapper.
В остальном - отличный ликбез
Единственная разница - при нехватке памяти
mallocвернётnullptr, аnew Fooвыкинет исключениеbad_alloc.
Чуть напомню, что можно собрать код без исключений или взывать отедльный noexecpt new и тогда также вернется nullptr при выделении памяти

C++: Айсберг времени жизни объектов