What is the purpose of pointer rebind?

Viewed 304

I trying to implement std::list (MSVC). And one thing I cannot understand:

template <class _Value_type, class _Voidptr> // voidptr? For what?
struct _List_node { // list node
    using value_type = _Value_type;
    using _Nodeptr   = _Rebind_pointer_t<_Voidptr, _List_node>; // what is the purpose of such rebind?
    ...
}

I understand the reason of allocator rebind, but pointer? Why should I use it and where?

UPD: I understand, what rebind is. I mean, why not just _Nodeptr*? Why do I need rebind? (Thanks to Evg)

2 Answers

The user instantiates the list with the value_type. For example for list<int> the value_type would be int. Also the list allocator (which can also be provided) allocates memory for objects of value_type's.

But the value_type is not what the list internally holds. The list holds internally the **Nodes** for which the value_type is a member of.

So to be able to convert allocation and pointer's from value_type to Node (which holds value_type and pointer to the next node at least) the rebind is used.

In contrary, this would not be need for a vector<int> for example. That's because the internal representation of vector will normaly hold internally the pointer to array of value_type's objects and that's int in this case. So no rebind needed here.

The answer to this question comes from allocators, too. Let's take a look at how _Rebind_pointer_t is defined:

template <class _Ptr, class _Ty>
using _Rebind_pointer_t = typename pointer_traits<_Ptr>::template rebind<_Ty>;

That is, we have

template <class _Value_type, class _Voidptr>
struct _List_node {
    using _Nodeptr = typename pointer_traits<_Voidptr>::template rebind<_List_node>;
    // ...
}

Now let's take a look at how _List_node is used:

using _Node = _List_node<_Ty, typename _Alty_traits::void_pointer>;

Effectively, we rebind allocator's void_pointer to _List_node pointer. This trick is needed to support allocators that use fancy pointers internally.


One such example can be found in Boost.Interprocess library. It has boost::interprocess::allocator:

An STL compatible allocator that uses a segment manager as memory source. The internal pointer type will of the same type (raw, smart) as typename SegmentManager::void_pointer type. This allows placing the allocator in shared memory, memory mapped-files, etc...

For example, we can write

namespace bi = boost::interprocess;
using Allocator = bi::allocator<int, bi::managed_shared_memory::segment_manager>;
std::list<int, Allocator> list(/* allocator object */);

Now std::allocator_traits<decltype(list)::allocator_type>::void_pointer will be not void* as with default allocator, but boost::interprocess::offset_ptr<void, ...>. As a result, _Nodeptr will be not _Nodeptr*, but boost::interprocess::offset_ptr<_Nodeptr, ...>.

Related