Gavin, in case my explanation in that earlier reply wasn't clear:
Given std::unique_ptr 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 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 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.
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