7

std::unique_ptr<T,D> is specified to store not a T* as you might expect, but an object of type std::unique_ptr<T,D>::pointer. This is defined to be basically D::pointer if such a type exists, and T* otherwise. Thus, you can customize the underlying raw pointer type by customizing your deleter appropriately.

When is it a good idea to do this? What is it used for? The only discussion I've been able to find is this note, which alludes to "better support[ing] containers and smart pointers in shared memory contexts", but that doesn't exactly shed a lot of light.

Geoff Romer
  • 2,358
  • 1
  • 18
  • 19

2 Answers2

7

The original motivation was to enable the use of boost::offset_ptr as the representation under unique_ptr, which would enable the use of unique_ptr in process-shared memory. Structures in process shared-memory should not contain pointers or references, only offsets.

I'm pleased to learn that the same feature can be useful in the Windows API.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
6

It is used when the deleter does not operate on T* values, obviously. That is why the deleter can specify a different data type than T*. A common use case is Win32 handles:

Using std::unique_ptr for Windows HANDLEs

Community
  • 1
  • 1
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Nice. I think the upshot is this: A `unique_ptr` behaves like a `T`, but its internal state is a `pointer`. In most cases, the pointer is `T *`, but it can be whatever you want, as long as its dereferencable to produce a `T` and nullable. – Kerrek SB Feb 08 '14 at 00:51
  • That said, with a small leap of faith you could use `std::unique_ptr p(..., FindVolumeClose)`, if you're willing to believe that `HANDLE = void *`. That'd require significantly less writing. – Kerrek SB Feb 08 '14 at 00:55
  • @KerrekSB: You should use `decltype()`, as an example in the other discussion showed: `std::unique_ptr p(..., FindVolumeClose)` – Remy Lebeau Feb 08 '14 at 01:11
  • For some reason I'm not a huge fan of `decltype` in this context. I'd much rather wrap the whole thing into a maker function: `std::unique_ptr make_unique_volume(...)`. With a type alias `unique_volume_handle` that would be quite readable, and the user would never need to know the deletion function. Just my personal opinion. (OK, that's actually getting very similar to the custom deleter again, but a function call is good for exception safety reasons.) – Kerrek SB Feb 08 '14 at 01:19
  • @KerrekSB: Why do you want to be explicit about the function signature? Let the compiler work out the signature for itself, thus avoiding any possibility of user error in typing the signature, especially if the signature error changes. – Remy Lebeau Feb 08 '14 at 01:45
  • Hang on, are you sure the `HANDLE` construction works? The standard says that `operator*` returns `*get()`, but also that its return type is `T &`, and that the return type of `get()` is `pointer`. That doesn't seem to add up. – Kerrek SB Feb 08 '14 at 01:54
  • (Another reason to prefer a deleter functor is that its call can be inlined, whereas a function pointer always requires indirection.) – Kerrek SB Feb 08 '14 at 01:56
  • You should never dereference a `HANDLE` (or any other Win32 handle type), so it does not make sense to use the `*` operator (or the `->` operator) when the `unique_ptr` is holding a `HANDLE` value. If you do, `*` will be a `void` (or a struct type if `STRICT` is defined), but `pointer` will be the original `HANDLE` type so `get()` will return a `HANDLE` when passed to API functions, as expected. – Remy Lebeau Feb 08 '14 at 02:12
  • I understand that `get()` works, but it feels that having `T = HANDLE` violates the specified types. At least with `T = void` it would be obvious that you can't use `operator*`, and it would still be consistent. – Kerrek SB Feb 08 '14 at 02:46