Reputation: 63154
Good evening everyone.
A code snippet will be worth a thousand words :
// Storage suitable for any of the listed instances
alignas(MaxAlign<Base, Derived1, Derived2>::value)
char storage[MaxSize<Base, Derived1, Derived2>::value];
// Instanciate one of the derived classes using placement new
new (storage) Derived2(3.14);
// Later...
// Recover a pointer to the base class
Base &ref = *reinterpret_cast<Base*> (storage);
// Use its (virtual) functions
ref.print();
// Destroy it when we're done.
ref.~Base();
As you can see, I want to access the instance only through its base class, and without actually storing the base class pointer. Note that in the second part, the Derived2
type information will be lost, so I'm left with only storage
and the guarantee that one derived instance is in it.
As placement new never adjusts the destination pointer, this boils down to using reinterpret_cast
to upcast to a base class. Now I know that this is dangerous, since the more appropriate static_cast
adjusts the pointer in some cases. [1]
And indeed, it triggers Undefined Behaviour. You'll find the full code here on Coliru (g++ 4.9.0), where it promptly crashes at runtime. Meanwhile on my PC (g++ 4.8.2), everything is fine. Note that on g++ 4.9, outputting both pointers before calling the functions shows identical values... and works.
So I tried to take the problem backwards : nudging the derived instance so that a pointer to Base
will be equal to storage
.
void *ptr = static_cast<Derived2*>(reinterpret_cast<Base*>(storage));
new (ptr) Derived2(3.14);
No luck. The thing still runs fine on g++ 4.8, and falls in flames on g++ 4.9.
Edit : come to think of it, the above isn't that smart. Because, what if the derived instance ends up before storage
... Ouch.
So my question is : is there a solution to what I'm trying to achieve ? Or, are the cases mentioned at [1] sufficiently well-defined that I can write code that will work with a subset of classes (like, no virtual inheritance) ?
Upvotes: 8
Views: 673
Reputation: 233
I've just modified your code a bit, it seems that reinterpret_cast is not the problem. The minimum code that could replicate this error is as such
Coliru link: http://coliru.stacked-crooked.com/a/dd9a633511a3d08d
#include <iostream>
struct Base {
virtual void print() {}
};
int main(int, char**) {
Base storage[1];
storage[0].print();
std::cout <<"Succeed";
return 0;
}
The sufficient conditions to trigger this error are
the "storage" variable is allocated on the stack as an array
the print() must be a virtual method
the compiler option should be -O2
If you use -O1, the program compiles and runs without a problem.
Besides, this error seems to only show up when compiled with g++ 4.9.0. It runs fine if compiled with VS2012/2013 or g++ 4.7.2 (you can test it on http://www.compileonline.com/compile_cpp_online.php)
Judging from the above, I think this may be a compiler specific problem.
Note: The actual output of the program given in this answer is different from the OP's. It does not show Segmentation Fault. However, when run successfully, it should print "Succeed", which is not shown when it is run on the Coliru.
Edit: Modified the code to replicate the error. No derived class needed.
Upvotes: 3