Comments 9
Если алгоритм позволяет получать итераторы только в момент первого использования, то
DEPEND()
не нужен, вместо*x
достаточноvec.begin()
. Покажите полезный пример, где получать итераторы заранее необходимо, иDEPEND()
защищает от ошибок.Если запретить использовать итераторы без обертки, как реализовывать алгоритмы на итераторах? Мне нужен
pivot = std::partition(left, right)
для текущих значений итераторов, потому что потом я их поменяю.
Пример с получением итераторов заранее показан в самом начале статьи. Может быть он и не очень "полезный", но это реальный код, в котором DEPEND() выполняет свою функцию.
Ваш второй пример с прямым использованием итератором std::partition(vec.begin(), vec.end()) в качестве аргументов функции не противоречит ограничениям на сохранение итераторов в переменные с помощью обертки DEPEND(), так как аргументы функции не создают переменных, которые могут протухнуть после из использования.
Разве что, может быть, если кроме итераторов диапазона в качестве аргумента передавать ещё и сам объект. Но тут уже я не могу придумать полезность подобной функции.
В примере из статьи x
и y
не используются до инвалидации. Под "полезным" примером я подразумеваю такой, где необходимо завести переменную-итератор, чтобы сохранить семантику кода. Например, работа с результатом std::remove_if()
, которая не сводится только к передаче его в vec.erase()
.
В моем примере аргументами std::partition()
выступают итераторы, которые будут меняться, о чем я написал, и результат — тоже итератор, который куда-то пойдет. Типичный код из середины quicksort, которая потом пойдет обрабатывать [left; pivot)
и [pivot; right)
. Разумеется, я не хочу вызывать std::partition()
на каждое обращение к pivot
, как это делает DEPEND()
.
Насколько я понимаю Вы пробуете сделать нечто близкое к предлагаемому профилю memory safety?
Одна из основных критик данного профиля в невозможности реализации без аннотаций, хоть авторы и утверждают что аннотаций будет минимум. Я скорее разделяю мнение что без аннотаций это невозможно, но мне кажется что большинство из них должно быть в описании структур данных, то есть в библиотечном коде, что вполне допустимо. Поэтому в Вашем первом опросе я голосовал что безопасность только с плагином невозможна, так как потребует поддержки всей стандартной библиотеки.
Третий подход рассмотренный в статье мне кажется не очень правильным. Всё-таки получение итератора до функции меняющей состояние вектора - это ошибка программиста, и желательно ее детектировать и исправить. На мой взгляд второй подход может быть терпимым в отношении ложных срабатываний, если добавить список разрешенных методов для разных концептов контейнеров. Например, у contiguous контейнера операции доступа к элементам не должны инвалидировать память, а resize и reserve точно инвалидируют.
Ещё интересно, есть ли у вас привязка времени жизни слабой ссылки (например, внутри итератора) к времени жизни сильной ссылки?(Надеюсь я правильно применил термины которые вы используете для плагина) В memory safety профиле если не ошибаюсь благодаря такому слежению помимо предотвращения доступа после освобождения памяти этот механизм используется для контроля алиасинга. Это конечно снова требует аннотаций, но от этого не сбежать.
Без аннотаций это действительно невозможно и по моему мнению. Но для их применения достаточно уже текущих стандартов С++ https://github.com/rsashka/memsafe
Что же касается ошибок программистов при использования итераторов, то я как раз и решал задачу по поиску подобных ошибок.
Слабые ссылки внутри итератора никак не используются, так как сами итераторы реализуются на другом принципе, тогда как самим слабым ссылкам вообще не требуется никакого контроля (контроль с помощью плагина нужен только для сильных ссылок).
Разве std::ranges не призваны, в том числе, решить и данную проблему?
Безопасная работа с итераторами в С++