В третий раз за несколько лет мне нужно найти навязчивый связанный список для проекта, который не позволяет повысить (спросить управление...).
В третий раз я обнаружил, что реализация интрузивного связанного списка у меня работает отлично, но мне действительно не нравится, что она использует поведение undefined, а именно при преобразовании указателя в список node в указатель на объект, содержащий этот список node.
Этот ужасный код выглядит так:
struct IntrusiveListNode {
IntrusiveListNode * next_;
IntrusiveListNode * prev_;
};
template <typename T, IntrusiveListNode T::*member>
class IntrusiveList {
// snip ...
private:
T & nodeToItem_(IntrusiveListNode & node) {
return *(T*)(((char*)&node)-((size_t)&(((T*)nullptr)->*member)));
}
IntrusiveListNode root_;
};
Мне все равно, как получается уродливый nodeToItem_
, но я хотел бы сохранить открытый интерфейс и синтаксис IntrusiveList
одинаковым. В частности, я хотел бы указать тип типа списка, используя IntrusiveList<Test, &Test::node_>
, а не IntrusiveList<Test, offsetof(Test, node_)>
.
Это почти 2016 - есть ли способ сделать это без вызова поведения undefined?
Изменить: В комментариях, которые я хочу обобщить здесь, было несколько предлагаемых решений (с участием разных структур списка):
-
Живой с помощью undefined, так как язык имеет, казалось бы, произвольные ограничения, которые предотвращают использование указателей элементов в обратном порядке.
-
Сохраните дополнительный указатель на содержащий класс в
IntrusiveListNode
. В настоящее время это, пожалуй, самое чистое решение (без необходимости изменения интерфейса), но требует третьего указателя в каждом списке node (возможны небольшие оптимизации). -
Вывести из
IntrusiveListNode
и использоватьstatic_cast
. В boost это версияbase_hook
интрузивного связанного списка. Я хотел бы придерживаться версииmember_hook
, чтобы избежать введения множественного наследования. -
Сохранить указатели на следующий и предыдущий классы, а не на следующий и предыдущий список node внутри
IntrusiveListNode
. Это затрудняет создание корня node в пределах интрузивного списка. Либо список должен включать полное инстанцированиеT
(что невозможно, например, еслиT
является абстрактным), либо конец списка должен быть нулевым указателем (который сломал бы--list.end()
, что позволило бы переслать итерацию только). -
У ускорения интрузивных списков есть версия
member_hook
, которая работает как-то, но реализация не была понята (и, возможно, она также зависит от поведения undefined).
Остается вопрос: возможно ли создать интрузивный список на основе членов с поддержкой двунаправленной итерации, поведение undefined и отсутствие "лишних" накладных расходов памяти?