Вы не можете изменить тип объекта. Вы можете уничтожить объект и создать что-то новое в той же памяти - это максимально близко к "изменению" типа объекта. По этой же причине компилятор некоторого кода фактически перечитывает vtable. Но проверьте это https://godbolt.org/z/Hmq_5Y - vtable читается только один раз. В общем - не может изменить тип, но может разрушать и создавать из пепла.
Отказ от ответственности: пожалуйста, пожалуйста, не делайте ничего подобного. Это ужасная идея, запутанная, трудная для понимания, компилятор может понять ее немного по-другому, и все пойдет наперекосяк. Если вы задаете такие вопросы, вы, конечно, не хотите применять их на практике. Задайте свою настоящую проблему, и мы ее исправим.
РЕДАКТИРОВАТЬ: это не летать:
#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
. Таким образом, он оптимизирует вызов виртуальной таблицы.
Этот код работает, хотя:
#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
. Что из этого сделает компилятор? Твоя догадка так же хороша как и моя.
В общем, если вы пишете код, который оставляет сомнения в том, как компилятор его интерпретирует (другими словами, вы попадаете в «серую зону», которая может быть понята по-разному вами, производителями компилятора, авторами языка и самим компилятором), вы напрашиваетесь на неприятности. . Пожалуйста, не делай этого. Спросите нас, зачем вам нужна подобная функция, и мы подскажем, как реализовать ее в соответствии с правилами языка или как обойти ее отсутствие.
person
Radosław Cybulski
schedule
16.06.2019
I've heard that it is not true from some speaker on CPPCon
было бы полезно добавить ссылку на соответствующий разговор, потому что тогда можно было бы сказать вам, что имел в виду человек. - person t.niese   schedule 17.06.2019