Search code examples
c++c++17unique-ptrunordered-mapstdmap

node-handle vs std::unique_ptr


Reading the documentation of the node-handle, I noticed that many features of the node-handle type can simply be emulated by a specialization of the std::unique_ptr. Indeed, the functionalities of the node-handle type are very similar to the that of the std::unique_ptr. It has only an interface more compliant with the features of associative containers, such as the key_type and mapped_type aliases and the functions to get a reference of the key/mapped value.

Are there any other advantages why the standard had introduced a node-handle type over a simple specialization of std::unique_ptr as node_type?


Solution

  • From a purely philosophical perspective, just because a type has a similar interface to another type does not mean that the type is redundant and should be replaced by the more general one.

    In this particular case, the types are just not equivalent. One of the main goals of node_handle is that you don't get to know the actual type of the node being used by the container. Not even as an opaque type alias; node_type is a specialization of node_handle, not the internal node typename.

    Also, consider that unique_ptr, by its nature, wraps a pointer to something. There is a pointer that was not owned at some point, then the unique_ptr owns it. Therefore, release is a reasonable function: it disowns the pointer without destroying it.

    That's not a reasonable function for node_handle. You did not create that node, and therefore you're not equipped to destroy it. That node is owned by a system that is intentionally opaque to the user. Any pointer held by it points to implementation-defined data structures, and there may be more than one of them.

    But that ignores the key feature of node_handle, the reason why it exists at all. The point of a node_handle is that you can extract an element from such a container, modify its key (and thus affecting where it would go in the container), and then reinsert it without allocating memory. To do that with unique_ptr<T> would require that T be some type the user understands and can talk to (and therefore used to modify the key). So, what is T?

    It can't be value_type for the container, as that makes the key type const and therefore non-modifiable. So it has to be some new type that gives you an interface to modify the key's value. Since it has to be some new type anyway... you may as well just make node_handle that type and save yourself a bunch of pain.

    Also, there is allocator behavior to consider. First, a node_handle also stores a copy of the container's allocator. Which unique_ptr can't really do.

    Second, stateful allocators may have special behavior when you move-assign from one container to another. Specifically, you may need to move the allocator when you do. But for many allocators, you may not. Therefore, node_handle needs to replicate this behavior. unique_ptr can't do that either.

    Now, you could essentially achieve all of this by creating a specialization of unique_ptr with a different interface conforming to the above, through the use of hidden (container-specific) typenames as template parameters. But... why bother? It's a different interface with a different, specialized purpose.

    So give it its own typename already. C++ is not running out of names.