I'm implementing a class that performs type erasure for small objects and have encountered a segmentation fault which I do not understand.
The following program:
#include <iostream>
#include <type_traits>
struct small_object
{
public:
template<class T>
small_object(const T& value)
{
new(&storage_) concrete<T>(value);
}
~small_object()
{
get_abstract().~abstract();
}
void print() const
{
// XXX crash here
get_abstract().print();
}
private:
struct abstract
{
virtual ~abstract(){}
virtual void print() const = 0;
};
template<class T>
struct concrete
{
concrete(const T& value) : value_(value) {}
void print() const
{
std::cout << value_ << std::endl;
}
T value_;
};
abstract& get_abstract()
{
return *reinterpret_cast<abstract*>(&storage_);
}
const abstract& get_abstract() const
{
return *reinterpret_cast<const abstract*>(&storage_);
}
typename std::aligned_storage<4 * sizeof(void*)> storage_;
};
int main()
{
small_object object(13);
// XXX i expect this line to print '13' to the terminal but it crashes
object.print();
return 0;
}
Crashes at the lines indicated by XXX
.
I believe the issue is that the virtual call to .print()
is not being dynamically dispatched correctly, but I don't understand why.
Can anyone tell what am I missing?
You didn't derive concrete<T>
from abstract
, so no vtable is being created when you construct the object using placement new
. Therefore, when you try to invoke the virtual function, it will fail; concrete<T>
and abstract
are actually completely unrelated types in this example.
I would recommend using the override
keyword if you're using C++11 or newer to allow the compiler to generate an error in cases like this.