In the following question one of the answers suggested that the dynamic type of an object cannot change: When may the dynamic type of a referred to object change?
However, I've heard that it is not true from some speaker on CPPCon or some other conference.
And indeed it does not seem to be true, because both GCC and Clang re-read vtable pointer on every loop iteration of the following example:
class A {
public:
virtual int GetVal() const = 0;
};
int f(const A& a){
int sum = 0;
for (int i = 0; i < 10; ++i) {
// re-reads vtable pointer for every new call to GetVal
sum += a.GetVal();
}
return sum;
}
However, if one adds the following:
class B final : public A {
public:
int GetVal() const override {
return 1;
}
};
int g(const B& b){
int sum = 0;
for (int i = 0; i < 10; ++i) {
sum += b.GetVal();
}
return sum;
}
then function g is simplified to return 10;, which is indeed expected because of final. It also suggests that the only possible place where the dynamic may change is inside GetVal.
I understand that re-reading vtable pointer is cheap, and asking mostly because of pure interest. What disables such compiler optimizations?
You can't change type of object. You can destroy object and create something new in the same memory - this is as closest as you can get to "changing" object type. This is also why for some code compiler will actually reread vtable. But check this one https://godbolt.org/z/Hmq_5Y - vtable is read only once. In general - can't change type, but can destroy and create from ashes.
Disclaimer: please, please, don't do anything like that. This is terrible idea, messy, hard to understand by anyone, compiler might understand it slightly differently and everything will pretty much go south. If you ask that kind of question, you certainly don't want to implement them in practise. Ask your real problem and we will fix it.
EDIT: this ain't fly:
Why? Because in main compiler sees
Bas final and compiler by language rule knows, that it controls lifetime of objectb. So it optimizes virtual table call.This code works tho:
I've used
newto allocate on heap, not on stack. This prevents compiler from assuming lifetime management ofb. Which in turn means it no longer can assume content ofbmight not change. Note, that trying to do raising from ashes inGetValmethod might not go well also -thisobject must live as at least long as call toGetVal. What will compiler make of it? Your guess is as good as mine.In general, if you write code, which leaves any doubt how compiler will interprete it (in other words you enter "grey area", which might be understood differently by you, compiler makes, language writers and compiler itself), you ask for troubles. Please, don't do that. Ask us, why you need feature like this and we will tell you, how either make it happen according to the language rules or how you can work around lack of it.