Is placement new of derived object within a vector / array defined to be base object legal
#include <iostream>
#include <memory>
#include <vector>
struct A
{
virtual ~A()
{
std::cout << "~A()" << std::endl;
}
};
struct B : public A
{
virtual ~B()
{
std::cout << "~B()" << std::endl;
}
};
int main()
{
std::vector<A> a(1);
a[0].~A();
:: new(std::addressof(a[0])) B();
}
Is this legal C++?
The question is related to the one here. The gist of the discussion there is that we are able to determine the type of the objects in a vector / array at compile time and so we do not have to do any dynamic dispatch. We can use this information to optimize whatever gcc is doing right now.
I filed an issue with gcc here and the argument against this optimization is that we could potentially have placement new changing the type stored in the vector / array, but we aren't sure if it is legal, hence this stackoverflow question.
Interestingly, gcc behaves differently from clang for arrays, and gcc has a different behavior with itself depending on the length of the array. It does the virtual dtor when len >= 3 but qualified dtor call when len < 3.
https://godbolt.org/z/f33Gh5EGM
Edit:
The question is purely "Is this legal C++". Any other information in the question is for context on why I'm asking such a question.
>= 3
might be just that, reallocation when the vector grows. Pretty sure this is ub – Cinellinew
? One could equally-well invoke UB by copying aB
object tostd::addressof(a[0])
. Putting an object of typecat
into a container of typedog
is UB, even though there are a number of ways you can do it. – FurthermoreB
is more thanA
and then you cannot possibly fit theB
s into the same memory as theA
s. There are solutions for what you want to do, but placement new seems to be not the right route – CinelliA
always containsA
by not putting anything else thanA
s in it. The compiler will help you to do this by not allowing you to put non-A
types into it in most cases. – Cidmemcpy(std::addressof(a[0]), &b, sizeof(B));
will overwrite the vtable of anA
at the destination, then: yes, it will. But so will using a placement new constructor - it will put aB
vtable in the space. – Furthermorestd::vector
does underneath. That is why yo have capacity and size for the vecotr. – Intermarry.emplace()
member of astd::vector
will do. – FurthermoreA
is always anA
but not aB
, the array is a red herring. Only aA*
can point to anA
or aB
. You need pointers or references for virtual dispatch. Btw I am a little confused by the motivation, on the one hand you want to avoid virtual dispatch, but then your issue is no virtual dispatch for the destructor. – CinelliB
, and it'll be UB if you let the vector clean itself up – Indo