Вы не можете изменить тип объекта. Вы можете уничтожить объект и создать что-то новое в той же памяти - это как можно ближе к «изменению» типа объекта. По этой же причине компилятор кода перечитывает vtable. Но отметьте это https://godbolt.org/z/Hmq_5Y - vtable читается только один раз. В целом - не может изменить тип, но может разрушать и создавать из пепла.
Отказ от ответственности : пожалуйста, не делайте ничего подобного. Это ужасная идея, грязная, которую трудно понять любому, компилятор может понять ее немного по-другому, и все пойдет на юг. Если вы зададите такой вопрос, вы определенно не захотите применять их на практике. Задайте свою реальную проблему, и мы исправим ее.
EDIT:
это не летать:
#include <iostream>
class A {
public:
virtual int GetVal() const = 0;
};
class C final : public A {
public:
int GetVal() const override {
return 0;
}
};
class B final : public A {
public:
int GetVal() const override {
const void* cptr = static_cast<const void*>(this);
this->~B();
void* ptr = const_cast<void*>(cptr);
new (ptr) C();
return 1;
}
};
int main () {
B b;
int sum = 0;
for (int i = 0; i < 10; ++i) {
sum += b.GetVal();
}
std::cout << sum << "\n";
return 0;
}
Почему? Потому что в основном компиляторе видит B
как окончательный , а компилятор по правилу языка знает, что он контролирует время жизни объекта b
. Таким образом, он оптимизирует вызов виртуальной таблицы.
Этот код работает tho:
#include <iostream>
class A {
public:
virtual ~A() = default;
virtual int GetVal() const = 0;
};
class C final : public A {
public:
int GetVal() const override {
return 0;
}
};
class B final : public A {
public:
int GetVal() const override {
return 1;
}
};
static void call(A *q, bool change) {
if (change) {
q->~A();
new (q) C();
}
std::cout << q->GetVal() << "\n";
}
int main () {
B *b = new B();
for (int i = 0; i < 10; ++i) {
call(b, i == 5);
}
return 0;
}
Я использовал new
для размещения в куче, а не в стеке. Это не позволяет компилятору принимать управление временем жизни b
. Это, в свою очередь, означает, что он больше не может предполагать, что содержимое b
может не измениться. Обратите внимание, что попытка сделать восстание из пепла в методе GetVal
также может не сработать - объект this
должен жить как минимум столько же времени, сколько вызов GetVal
. Что из этого сделает компилятор? Ваше предположение так же хорошо, как и мое.
В общем, если вы пишете код, который не вызывает сомнений в том, как компилятор будет его интерпретировать (другими словами, вы вводите «серую область», которая может быть по-разному воспринята вами, создателем компилятора, создателями языка и самим компилятором), вы просить неприятностей. Пожалуйста, не делай этого. Спросите нас, зачем вам нужна такая функция, и мы расскажем вам, как сделать это в соответствии с языковыми правилами или как обойти эту проблему.