
Ion GaztaƱaga wrote:
Semantics might be a bit more clear for default_delete, as it calls delete. std::is_polymorphic might not be very accurate, maybe std::has_virtual_destructor.
has_virtual_destructor is the correct one. It's a later addition and the implementation on which I prototyped probably didn't have it.
A user-defined deleter could be a no-op or an operation that can properly recycle (link it in a intrusive list, etc.) the object even if it has no virtual destructor, just because it doesn't call the destructor.
In principle, we should leave everything to the deleter. The problem is that in practice things like the following often occur: struct X { }; struct Y: X { std::string s_; }; struct D { template<class T> void operator()( T* p ) const { delete p; } }; int main() { unique_ptr<Y, D> p1( new Y ); unique_ptr<X, D> p2( p1 ); // legal? } The array case is similar: struct E { template<class T> void operator()( T* p ) const { delete[] p; } }; int main() { unique_ptr<Y[], E> p1( new Y[3] ); unique_ptr<X[], E> p2( p1 ); // legal? } although here we do have grounds on which to reject - we know that operator[] will not work. I agree that the deleter may in principle not delete, and therefore the non-array conversion case may be safe. On the other hand, the obvious shared_ptr example, the one with the null deleter, doesn't translate, and I'm not sure I can come up with another (for which conversions are desirable).