Может ли связанный список XOR быть реализован на С++, не вызывая поведения undefined?

An Связанный список XOR - это модифицированная версия обычного дважды связанного списка, в котором каждый node хранит только один "указатель" вместо двух. Этот "указатель" состоит из XOR следующего и предыдущего указателей. Для перемещения по списку требуется два указателя - один для текущего node и один для следующего или предыдущего node. Для перемещения вперед предыдущий node адрес имеет XORed с "указателем", хранящимся в текущем node, отображая истинный "следующий" указатель.

Стандарт С++ вызывает кучу операций над указателями и целыми числами, что приводит к поведению undefined - например, вы не можете гарантировать, что установка определенного бита в число не приведет к тому, что аппаратное обеспечение вызовет прерывание, поэтому в некоторых в случаях, когда результат бит может быть undefined.

Мой вопрос следующий: существует ли реализация С++ связанного с XOR списка, которая не приводит к поведению undefined?

Ответ 1

Мой вопрос следующий: существует ли реализация С++ связанного с XOR списка, которая не приводит к поведению undefined?

Если по "есть ли реализация" вы имеете в виду "уже написано", то я не знаю. Если вы имеете в виду "возможно ли написать одно", тогда да, но могут быть некоторые предостережения о переносимости.

Вы можете погладить свой сердечный контент, если перед тем, как начать, вы конвертируете оба указателя в uintptr_t и сохраните этот тип в node вместо указателя. Побитовые операции с неподписанными типами никогда не приводят к поведению undefined.

Однако uintptr_t является необязательным типом, поэтому он не является полностью переносимым. Нет требования, чтобы реализация С++ фактически имела целочисленный тип, способный представлять адрес. Если в реализации нет uintptr_t, тогда код разрешается компилировать с помощью диагностики, и в этом случае его поведение выходит за рамки стандарта. Не уверен, считаете ли вы, что нарушение "без UB" или нет. Я имею в виду, серьезно, компилятор, который позволяет использовать код, который использует типы undefined?; -)

Чтобы избежать uintptr_t, я думаю, что вы могли бы сделать свой бит-twiddling на массиве sizeof(node*) unsigned chars. Указатели являются типами POD и поэтому могут быть скопированы, повернуты и искалечены при условии, что представление объекта будет восстановлено до его первоначального состояния перед использованием в качестве указателя.

Обратите также внимание, что если ваша реализация на С++ имеет сборщик мусора, тогда преобразование в целое число /xor/xor -back-again/convert-to-pointer не обязательно останавливает собираемый объект (поскольку это приводит к "небезопасный производный указатель" ). Поэтому для переносимости вы также должны убедиться, что результирующие указатели действительны. Два способа сделать это:

  • наберите declare_reachable.
  • Используйте реализацию с непринужденной безопасностью указателя (это определяется реализацией, если это так, и вы можете проверить ее с помощью get_pointer_safety() с учетом того, что непринужденной реализации разрешено ложно утверждать, что она является строгой).

Вы можете подумать, что есть третий способ (хотя и тот, который побеждает цель связанного списка XOR, если у вас его нет):

  • сохранить отдельный контейнер всех значений указателя

Это не гарантируется. Недопустимый вывод указателя недействителен, даже если он оказывается равным безопасно полученному указателю (3.7.4.3/4). Я тоже был удивлен.