C++ gurus. Need your help with this little head scratcher:
#include <iostream>
struct B{
virtual ~B() = default;
virtual void talk() { std::cout << "Be-e-e\n"; }
};
struct D:B{
void talk() override { std::cout << "Duh\n"; }
~D() { std::cout << "~D()\n"; }
};
int main(){
B b{}; // vptr points to B
new (&b) D; // vptr now points to D
b.talk(); // "Be-e-e" (why? shouldn't the vptr be used?)
b = D{}; // "~D()" (why? shouldn't the copying be elided?)
b.talk(); // "Be-e-e"
B*b1{new D};
b1->talk(); // "Duh"
delete b1; // "~D()"
return 0;
}
The code is pretty straightforward: have a base object on stack, placement-new a derived one into it (yes, eeew, but bear with me) and calling a virtual method, expecting a derived's output to be printed.
Actual output
The code above produces the following output:
Be-e-e ~D() Be-e-e Duh ~D()
That behavior is universally observed on MSVC, gcc, clang, and a few online compilers I tried it on (which is an extremely strong indication that it is I who am wrong).
Part 1
The placement-new re-news a derived-type object into the base-type memory. And that updates the vptr to point to the derived-type's vtable (directly observed in the debugger).
Main question: is that the expected behavior? (I want to say "yes" so if it is not - please explain to me)
I want to believe that performing a placement-new (provided there's enough memory for the derived-type object) should in-place initialize a brand new object of the derived-type.
If my understanding is correct, the first b.talk()
should output "Duh"
as the object now is of derived-type. Why is it still printing "Be-e-e"
?
Assigning a derived-type object into the base-type object (in addition to causing object splicing) does not copy the vptr, so that second "Be-e-e"
output is expected, provided the object is still of the base-type when we get to that line of code.
Part 2
Why is there a ~D()
call in the b = D{};
assignment? Isn't it a temporary that should be copy-elided with no need for a destructor call on that temporary?
Part 3
The last block of code that uses pointers works "as expected" and is just here for sanity check