In addition to Mark Ransom's answer , unique_ptr<X, D> may not even store X* .
If the deleter determines the type D::pointer , then what is stored and which may not be a real pointer, it must satisfy the requirements of NullablePointer and (if unique_ptr<X,D>::get() is called) has an operator* that returns X& but listing support for other types is not required.
unique_ptr quite flexible and does not necessarily behave very much like a built-in pointer type.
As requested, here is an example where the stored type is not a pointer, and therefore casting is not possible. This is a bit far-fetched, but completes the created database API (defined as the C API) in the C ++ RAII API. The OpaqueDbHandle type satisfies the requirements of NullablePointer , but only stores an integer that is used as a key to search for the actual database connection through a specific mapping defined by the implementation. I donβt show it as an example of great design, as an example of using unique_ptr to manage an unique_ptr -copied movable resource, which is not a dynamically allocated pointer, where βdeleterβ does not just call the destructor and free memory when unique_ptr goes beyond.
#include <memory> // native database API extern "C" { struct Db; int db_query(Db*, const char*); Db* db_connect(); void db_disconnect(Db*); } // wrapper API class OpaqueDbHandle { public: explicit OpaqueDbHandle(int id) : id(id) { } OpaqueDbHandle(std::nullptr_t) { } OpaqueDbHandle() = default; OpaqueDbHandle(const OpaqueDbHandle&) = default; OpaqueDbHandle& operator=(const OpaqueDbHandle&) = default; OpaqueDbHandle& operator=(std::nullptr_t) { id = -1; return *this; } Db& operator*() const; explicit operator bool() const { return id > 0; } friend bool operator==(const OpaqueDbHandle& l, const OpaqueDbHandle& r) { return l.id == r.id; } private: friend class DbDeleter; int id = -1; }; inline bool operator!=(const OpaqueDbHandle& l, const OpaqueDbHandle& r) { return !(l == r); } struct DbDeleter { typedef OpaqueDbHandle pointer; void operator()(pointer p) const; }; typedef std::unique_ptr<Db, DbDeleter> safe_db_handle; safe_db_handle safe_connect(); int main() { auto db_handle = safe_connect(); (void) db_query(&*db_handle, "SHOW TABLES"); } // defined in some shared library namespace { std::map<int, Db*> connections; // all active DB connections std::list<int> unused_connections; // currently unused ones int next_id = 0; const unsigned cache_unused_threshold = 10; } Db& OpaqueDbHandle::operator*() const { return connections[id]; } safe_db_handle safe_connect() { int id; if (!unused_connections.empty()) { id = unused_connections.back(); unused_connections.pop_back(); } else { id = next_id++; connections[id] = db_connect(); } return safe_db_handle( OpaqueDbHandle(id) ); } void DbDeleter::operator()(DbDeleter::pointer p) const { if (unused_connections.size() >= cache_unused_threshold) { db_disconnect(&*p); connections.erase(p.id); } else unused_connections.push_back(p.id); }
Jonathan Wakely Jun 12 '12 at 18:51 2012-06-12 18:51
source share