Matt Clarkson
Matt Clarkson

Reputation: 14416

std::unique_ptr<void> not accepted by gcc 4.9.0

So gcc 4.9.0 has implemented a static_assertion 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

Answers (1)

Jonathan Wakely
Jonathan Wakely

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

Related Questions