Gavin, in case my explanation in that earlier reply wasn't clear: Given std::unique_ptr<Foo, D> p; 1. Users know that *p and p->x do the right thing by Foo. 2. p.get() will get them a D::pointer. This D::pointer might be Foo*, or it might be offset_ptr<Foo> or it might be alloc_ptr<offset_ptr<Foo>> or any other pointer-like type. To get a Foo* out of p.get() regardless of which of the above three it returns, you can call: Foo* q = std::to_address(p.get()); // C++20 in std, C++03 in boost Or, if you know that p is not a null pointer, you can also use: if (p) { Foo* q = std::addressof(*p); // C++11 in std, C++03 in boost } std::unique_ptr<T, D> was always designed for this (since C++11). Because not all pointers we use are raw pointers. An Allocator::pointer might not be X*, it might be offset_ptr<X>, or it might be aligned_ptr<offset_ptr<X>>. This property of unique_ptr is also unrelated to allocate_unique (which just leverages this with a fancy-pointer around the Allocator::pointer which in turn might be a fancy-pointer). But even today, users are using unique_ptr with non-raw pointers (like Allocators where Allocator::pointer is offset_ptr<X> instead of X*). They don't assume that p.get() returns a raw X* pointer. This might be a lesser known property of unique_ptr to some (especially those that do not work much with custom allocators), but this is the way it has always been and intentionally so. Glen