Я пытаюсь реализовать std::list
(MSVC). И одного не могу понять:
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?
...
}
Я понимаю причину перепривязки аллокатора, но указатель? Зачем мне его использовать и где?
UPD: Я понял, что такое ребинд. Я имею в виду, почему бы не просто _Nodeptr*
? Зачем мне ребинд? (спасибо Евге)
Пользователь создает список с помощью value_type.
Например, для list<int>
value_type
будет int
.
Также распределитель списка (который также может быть предоставлен) выделяет память для объектов value_type
.
Но value_type
— это не то, что содержится внутри списка.
Список содержит внутри **Nodes**
, членом которого является value_type.
Таким образом, чтобы иметь возможность преобразовать выделение и указатель из value_type
в Node
(который содержит как минимум value_type и указатель на следующий узел), используется повторное связывание.
Наоборот, это не нужно, например, для vector<int>
.
Это связано с тем, что внутреннее представление вектора обычно содержит внутренний указатель на массив объектов value_type, и в данном случае это int. Так что здесь не нужна перепрошивка.
Настоящий вопрос: почему бы не просто _Nodeptr*
? Зачем нужен ребинд?
Да, мне нужно уточнить вопрос
@Evg: Потому что никто не говорит, что такое указатель на самом деле. Это может быть просто Node*, но теоретически это может быть и какой-то причудливый указатель. Итак, в основном: я хочу, чтобы Node был таким же указателем, как и value_type, независимо от того, какой это указатель.
@StPiere, ну, теперь я понял эту идею. Можете ли вы написать пример, пожалуйста? (ссылка или что-то))
@Shamil: ты уже сам написал пример. Просто посмотрите на некоторые реализации списка stl: msvc, libstdc++ и т. д. Для причудливого указателя вы можете поискать в Google и посмотреть, например: boost::interprocess::offset_ptr
Ответ на этот вопрос исходит и от аллокаторов. Давайте посмотрим, как _Rebind_pointer_t
определяется :
template <class _Ptr, class _Ty>
using _Rebind_pointer_t = typename pointer_traits<_Ptr>::template rebind<_Ty>;
То есть у нас есть
template <class _Value_type, class _Voidptr>
struct _List_node {
using _Nodeptr = typename pointer_traits<_Voidptr>::template rebind<_List_node>;
// ...
}
Теперь давайте посмотрим, как _List_node
используется :
using _Node = _List_node<_Ty, typename _Alty_traits::void_pointer>;
По сути, мы перепривязываем указатель void_pointer
к _List_node
распределителя. Этот трюк необходим для поддержки распределителей, которые используют причудливые указатели внутри.
Один такой пример можно найти в библиотеке Boost.Interprocess. Он имеет boost::interprocess::allocator:
Распределитель, совместимый с STL, который использует диспетчер сегментов в качестве источника памяти. Тип внутреннего указателя будет того же типа (необработанный, умный), что и тип
typename SegmentManager::void_pointer
. Это позволяет размещать распределитель в разделяемой памяти, отображаемых файлах памяти и т. д.
Например, мы можем написать
namespace bi = boost::interprocess;
using Allocator = bi::allocator<int, bi::managed_shared_memory::segment_manager>;
std::list<int, Allocator> list(/* allocator object */);
Теперь std::allocator_traits<decltype(list)::allocator_type>::void_pointer
будет не void*
, как с аллокатором по умолчанию, а boost::interprocess::offset_ptr<void, ...>
. В итоге _Nodeptr
будет не _Nodeptr*
, а boost::interprocess::offset_ptr<_Nodeptr, ...>
.
Спасибо. Вам помогли понять предыдущий ответ)
По теме: blog.nuggetwheat.org/index.php/2015/09/01/…