Почему разыменование указателя на извлеченный узел std :: set будет неопределенным поведением? - PullRequest
0 голосов
/ 28 февраля 2019

В своей компании я провел молниеносную беседу о новом (C ++ 17) интерфейсе сращивания ассоциативных контейнеров.Я продемонстрировал std::set::extract, а затем спросил, что будет с итераторами и указателями на извлеченный элемент.Они поймали меня не на той ноге, и я не смог ответить на вопрос, но посмотрел его сразу после выступления.

[associative.reqmts] 21.2.6.10 в текущем проекте стандартагласит:

extract члены делают недействительными только итераторы для удаленного элемента;указатели и ссылки на удаленный элемент остаются действительными. Однако доступ к элементу через такие указатели и ссылки, когда элемент принадлежит node_­type, является неопределенным поведением. Ссылки и указатели на элемент, полученный, когда он принадлежит node_­type, становятся недействительными, еслиэлемент успешно вставлен.

(предложение P0083R3 уже содержит эту формулировку)

Теперь выделенная часть действительно меня смущает.Я понимаю концепцию действительного, но не разыменованного указателя (nullptr) или итератора (конечный итератор).Я нашел «определение» действительных указателей от David Vandevoorde и узнал, что существуют также действительные, но не разыменовываемые указатели, которые не являются nullptr.(а именно указатель один за существующим объектом)

При всем этом моя ментальная модель происходящего выглядит следующим образом:

  1. Получается указатель на элемент в наборе,поэтому указатель действителен.Разыменование этого указателя определено и дает доступ к элементу в наборе.
  2. Теперь один и тот же элемент извлекается из набора.Концептуально элемент не копируется, не перемещается и не изменяется иным образом, связанный с ним узел просто удаляется из внутреннего дерева, с которым set управляет его данными.Оставшееся дерево, возможно, потребуется перебалансировать.Возвращенный node_handle становится владельцем узла потерянного дерева.

Как и в стандарте, указатель, полученный в 1), остается действительным и не может быть изменен на extract, так что это также поддерживает эту ментальную модель,Однако в этой модели нет причин, по которым разыменование указателя внезапно будет неопределенным.Следовательно, с g ++ на coliru он работает так, как я и ожидал .(это никоим образом не является доказательством)

Слабость, которую стандарт дает разработчикам библиотек, кажется излишне большой.Чего мне не хватает?Я только вижу, что при извлечении их константа заданных значений отбрасывается, но я не понимаю, как это могло бы повлиять.

То же рассуждение применимо к случаю вставки, упомянутому в последнем цитируемом предложении.

1 Ответ

0 голосов
/ 28 февраля 2019

В вашей ментальной модели отсутствует тот факт, что удаление константы, строго говоря, должно быть определенным реализацией.

node_handle должно стать владельцем другой объект, но с помощью некоторого волшебства, определенного реализацией, этот изменяемый объект возникает без создания, имея то же значение и хранилище, что и исходный объект const.

Аналогично, когда он вставляется внабор с совместимым распределителем, который преобразует изменяемый объект, принадлежащий node_handle, обратно в исходный объект const.

Это неопределенное поведение, потому что объект const прекратил существование, а "оно" принадлежитnode_handle, но затем он снова начинает существовать при повторной вставке.

Это тот же тип рассуждений, который делает неопределенным поведение использовать node_handle с карты, если у вас есть пользовательскийспециализация std::pair<const K, V> или std::pair<K, V>.Вы не хотите ограничивать реализацию тем, как «магия» используется для достижения всего этого, поэтому вы делаете все, что будет наблюдать «магическое» неопределенное поведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...