Why is std :: unique_ptr implicitly converted to T * and const T *?

If I wrote this myself, I would do something like:

template<typename T, typename Dtor = std::default_delete<T> > class Uptr : private Dtor { T* vl_; public: explicit Uptr(T* vl = nullptr) noexcept : vl_(vl) {} ~Uptr() noexcept { Dtor::operator()(vl_); } Uptr& swap(Uptr& o) noexcept { T* tmp; tmp = vl_; vl_=o.vl_; o.vl_ = tmp; } Uptr& operator=(Uptr&& o) noexcept { o.swap(*this); } Uptr& operator=(nullptr_t) noexcept { vl_=nullptr; return *this; } Uptr(Uptr&& o) noexcept : Uptr(nullptr) { *this = std::move(o); } Uptr(const Uptr& o) = delete; Uptr& operator=(const Uptr& o) = delete; operator T*() noexcept { return vl_; } operator const T*() const noexcept { return vl_; } T* release() noexcept { T* ret = vl_; vl_=nullptr; return ret; } const Dtor& deleter() const noexcept { return *(static_cast<Dtor*>(this)); } Dtor& deleter() noexcept { return *(static_cast<Dtor*>(this)); } }; 

And get rid of the need to define get() and operators * , -> and [] .

What happened to the implicit conversion in this case?

+8
c ++ c ++ 11
source share
3 answers

I don't think your question is specific to unique_ptr , but rather asks about smart pointers in general.

Coat of arms Sutter wrote about this a long time ago . Apparently, this will allow you to write logically erroneous code, for example:

 unique_ptr<something> p; ... delete p; // p is a smart pointer - probably not what you want. 

and other similar codes.

+14
source share

There are some annoying errors that can happen, as others have pointed out, for example

 T* f() { unique_ptr p{...}; return p; } // oops unique_ptr p{...}; delete p; // oops 

and

 unique_ptr get_up(); shared_ptr sp{get_up()}; // cool, this works: promote UP to SP // does it work the other way around? shared_ptr get_sp(); unique_ptr p{get_sp()}; // compiles, then yes! eh, wait.. BOOM! 

And so on. This is simply safer, given that this language is intended for people to evade these traps by making everything explicit in such cases.

+8
source share

I do not have an official answer, but I have an idea why this does not make much sense. The whole idea of unique_ptr is to be a strongly typed (i.e. compiler) way of having unique ownership of an object. Therefore, the semantics of displacement.

In this context, it makes sense that unique_ptr requires you to explicitly request your raw pointer, because as soon as you have the original value, you can easily break everything: you could delete it, you could pass it to another smart pointer ( which, in turn, will delete it at some point), or do pointer arithmetic and randomly work with the result (which may not indicate the allocated memory) or something like that. Some of them can immediately lead to compiler errors, others can lead to undefined behavior. For a high-level class such as unique_ptr , this is undesirable, at least it is implicit. Of course, you can do the same with .get() , but in this case you know that you are using the original value of the pointer and should not free it.

The link posted by Ami is also very relevant, but I think another example is better cited:

 unique_ptr p( new widget ); ... use( p + 42 ); // error (maybe he meant "*p + 42"?) // but if implicit conversion to * were allowed, would silently compile -- urk 

The philosophy of C ++ is that public functions that can work with this type belong to the interface of this type. Managing and restricting an interface of a type that is implicitly converted to a pointer is almost impossible. The intentionally restrictive nature of smart pointers will be completely deconstructed, resulting in code error.

+6
source share

All Articles