class C
{
public:
virtual ~C() {}
virtual void switch_me() = 0;
};
class C1 : public C
{
public:
C1() : b(true) { std::cout << "C1\n"; }
~C1() { std::cout << "~C1\n"; }
private:
void switch_me();
bool b;
};
class C2 : public C
{
public:
C2() : i(1) { std::cout << "C2\n"; }
~C2() { std::cout << "~C2\n"; }
private:
void switch_me();
int i;
};
void C1::switch_me()
{
this->~C1(); // lifetime of *this ends here
std::cout << "blih\n"; // execute some code that does
// not attempt to access object
new(this) C2(); // create a C2 instance in-place
}
void C2::switch_me()
{
this->~C2(); // lifetime of *this ends here
std::cout << "blah\n"; // execute some code...
new(this) C1(); // create a C1 instance in-place
}
class Cnt
{
public:
Cnt() { new(&storage) C1(); }
~Cnt() { (*this)->~C(); }
C* operator->() { return reinterpret_cast<C*>(&storage); }
private:
char storage[std::max(sizeof(C1),sizeof(C2))];
};
int main()
{
Cnt c;
c->switch_me();
c->switch_me();
return 0;
}
У вас нет неопределенного поведения в отношении switch_me
функция: вы не получаете доступ к объекту каким-либо образом после уничтожения, и следующий доступ происходит на новом объекте. У вас может быть UB, если вы сохраните указатель и ссылку на C
объект возвращен vy operator->
и использовать его после вызова switch_me
в 3,8 / 7:
Если по истечении времени жизни объекта и до повторного использования или освобождения хранилища, которое занимал объект, в месте хранения, которое занимал исходный объект, создается новый объект, указатель, указывающий на исходный объект, ссылка, которая ссылка на исходный объект, или имя исходного объекта будет автоматически ссылаться на новый объект и, как только начнется время жизни нового объекта, может использоваться для управления новым объектом, если:
- хранилище для нового объекта точно перекрывает хранилище
место, которое занимал исходный объект, и- новый объект
того же типа, что и исходный объект (игнорируя верхний уровень
cv-квалификаторы) и- тип исходного объекта не
квалифицированный const, и, если тип класса, не содержит
нестатический член данных, тип которого является константным или является ссылкой
тип и- исходный объект был наиболее производным объектом (1.8) из
тип T и новый объект является наиболее производным объектом типа T (что
то есть они не являются подобъектами базового класса).
У вас есть UB в другом месте, а именно, ваше хранилище. Он имеет более слабое выравнивание, чем объект, который вы хотите поместить в него, и это может вызвать проблемы с выравниванием. использование alignas
Ключевое слово для определения желаемого выравнивания:
alignas(C1) alignas(C2) char storage[std::max(sizeof(C1),sizeof(C2))];
Если к одному объявлению применяются два спецификатора выравнивания, используется больший.
Других решений пока нет …