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

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

По теме: blog.nuggetwheat.org/index.php/2015/09/01/…

Evg 12.12.2020 09:30
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
446
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Пользователь создает список с помощью 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 12.12.2020 09:40

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

Shamil Mukhetdinov 12.12.2020 09:41

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

StPiere 12.12.2020 09:41

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

Shamil Mukhetdinov 12.12.2020 09:45

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

StPiere 12.12.2020 09:46
Ответ принят как подходящий

Ответ на этот вопрос исходит и от аллокаторов. Давайте посмотрим, как _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, ...>.

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

Shamil Mukhetdinov 13.12.2020 08:50

Другие вопросы по теме