Какова цель повторного связывания указателя?

#c #pointers #traits

#c #указатели #Трейты

Вопрос:

Я пытаюсь реализовать 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* ? Зачем мне нужно повторное связывание? (Благодаря Evg)

Комментарии:

1. Связанные: blog.nuggetwheat.org/index.php/2015/09/01 /…

Ответ №1:

Ответ на этот вопрос также исходит от распределителей. Давайте посмотрим, как _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 .Межпроцессная библиотека. Он имеет boost::interprocess::allocator:

Совместимый с STL распределитель, который использует диспетчер сегментов в качестве источника памяти. Тип внутреннего указателя будет того же типа (raw, smart), typename SegmentManager::void_pointer что и type . Это позволяет размещать распределитель в общей памяти, файлах с отображением памяти и т. Д…

Например, мы можем написать

 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, ...> .

Комментарии:

1. Спасибо. Вам помогли понять предыдущий ответ)

Ответ №2:

Пользователь создает экземпляр списка с value_type. помощью, например, для list<int> value_type would be int . Также распределитель списков (который также может быть предоставлен) выделяет память для объектов value_type ‘s.

Но value_type это не то, что содержит список внутри. Список содержит внутренне **Nodes** значение, для которого value_type является членом.

Таким образом, чтобы иметь возможность преобразовывать выделение и указатель из value_type в Node (который содержит value_type и указатель, по крайней мере, на следующий узел), используется повторная привязка.

Напротив, это не было vector<int> бы необходимо для примера. Это потому, что внутреннее представление vector обычно будет содержать внутри указатель на массив объектов value_type, и в данном случае это int . Поэтому здесь не требуется повторная привязка.

Комментарии:

1. Реальный вопрос: почему не просто _Nodeptr* ? Зачем вам нужна повторная привязка?

2. Да, мне нужно уточнить вопрос

3. @Evg: Потому что никто не говорит, что такое указатель на самом деле. Это может быть просто узел *, но теоретически это может быть и какой-то причудливый указатель. Итак, в основном: я хочу, чтобы узел был того же типа poitner, что и value_type , независимо от того, какой это указатель.

4. @StPiere, ну, теперь я понимаю идею. Не могли бы вы написать пример, пожалуйста? (ссылка или что-то еще))

5. @Shamil: вы уже сами написали пример. Просто посмотрите на некоторые реализации списка stl: msvc, libstdc и т.д. Для получения какого-то необычного указателя вы можете поискать в Google и посмотреть, например: boost::interprocess::offset_ptr