Reputation: 14416
So gcc 4.9.0
has implemented a static_assert
ion that the type is not void
to be correctly conforming to the standard. Which is great, all for standards conformance.
I have a variant type that stored the data under a std::unique_ptr<void>
which now doesn't work. The easy fix is to just change it to a std::shared_ptr<void>
and it compiles straight away. The better fix is to provide the deleter functor.
Is the following a safe way of fixing up the unique_ptr
? Will this exhibit undefined behaviour with certain polymorphic types?
#include <memory>
#include <iostream>
template<typename T>
void Deleter(void * p) {
delete reinterpret_cast<T*>(p);
}
int main() {
const std::unique_ptr<void, void(*)(void*)> v(new int(199), Deleter<int>);
std::cout << *reinterpret_cast<int*>(v.get()) << std::endl;
return 0;
}
Upvotes: 3
Views: 660
Reputation: 171263
Will this exhibit undefined behaviour with certain polymorphic types?
If you use unique_ptr<void, void(*)(void*)>(new X(...), Deleter<X>)
that will be correct for any X
, because the deleter uses the correct dynamic type of the object.
If you used unique_ptr<void, void(*)(void*)>(new Derived(...), Deleter<Base>)
that will have undefined behaviour (for both polymorphic and non-polymorphic types) if Base
does not have a virtual destructor, or if the Base
sub-object is not at the same address as the Derived
object that contains it.
However, you should use static_cast
not reinterpret_cast
in both places. You should prefer to use the weakest cast that will work for a given situation, reinterpret_cast
is a sledgehammer.
To make the code less error-prone I would write a helper function so you only ever name the type once, something like:
template<typename T, typename... Args>
std::unique_ptr<void, void(*)(void*)>
make_unique_void_ptr(Args&&... args)
{
using Ptr = std::unique_ptr<void, void(*)(void*)>;
return Ptr{ new T(std::forward<Args>(args)...), Deleter<T> };
}
Upvotes: 9