Вот в C++ принято что нет проверки выхода за пределы массива. Т.е. обратились по индексу 6, в то время как элементов всего 6 — вам даже не сообщат об ошибке. Считается что C++-разработчики не могут допустить таких ошибок и если ты допустил — то вон из профессии, таким как ты тут не место (ну или хотя бы никому не говори об этом).
Добавлю оговорку. Есть at(), который как бы на отвяжись добавили. Однако же он не решает проблему, т.к. даже при банальном копировании данных он вызываться не будет (см. пример ниже).
Это можно оправдать скоростью работы, ведь каждая проверка требует доп. инструкций процессора. Было бы неплохо иметь возможность хотя бы применять такие проверки по некой опции, к примеру добавить флаг сборки или что-то подобное. Когда уже отладил — флаг можно и убрать, в принципе. Более того — скорость не всегда критична и не всегда на первом месте — иногда важнее точность работы кода и быстрое обнаружение ошибок.
И вопрос такой. В принципе то C++ не виноват, он же не запрещает проверки выхода за пределы. Однако же виновата философия, которая говорит что такие проверки нужны только школьникам, что настоящие программисты не ошибаются. И я подумал грешным делом — а что если просто добавить обертки для стандартных классов, которые выполняют такие проверки? Не слишком ли смелое решение?
Как оказалось, дело это не такое уж тривиальное. Вот, к примеру, GPT выдал для проверки c std::ranges — но это не всегда работает как нужно:
Посмотрите мой пример — это никак не повлияет на использование стандартных алгоритмов, к примеру деже если просто скопируете и укажете индекс более чем доступно.
Здравствуйте , Shmj, Вы писали:
S>На мысль навела статья.
S>Ну и сам вопрос. Может кто-то уже думал в этом направлении и есть такие безопасные обертки?
Тысячи их. Python например
Большая часть кода не является критически важной по скорости и может выполняться на языках которые не так быстры, но зато без UB, требуют меньше писанины, проще, удобней и дешевле. А вот те части которые требуют интенсивных вычислений можете хоть на фортране писать или использовать готовые оптимизированные библиотеки для типовых вычислительных задач. Для повышения ЧСВ быстрых вставок можно terra пощупать.
Здравствуйте, kov_serg, Вы писали:
_>Тысячи их. Python например
Ну в Rust по умолчанию есть проверки и наоборот, чтобы сделать что-то без проверок — нужно вызывать спец. версии методов. При этом он не слишком медленнее C++ как-то.
Возможно и в C++ можно добавить такие проверки, хотя бы опционально на этапе отладки?
На мой взгляд проблема несколько глубже, чем просто добавить проверки.
Действительно проверки будут генерировать медленный код. Но, очевидно, во многих случаях компилятор может эти проверки убрать, если докажет, что они избыточны.
Таким образом правильный способ решения этой проблемы: спроектировать язык, компилятор и стандартную библиотеку таким образом, чтобы: в стандартной библиотеке были проверки, но в то же время типовой код позволял бы компилятору выбрасывать эти проверки, не жертвуя корректностью.
Я не знаю, насколько создатели Rust пытались следовать этому подходу, да и в принципе не знаю, насколько это возможно, но по факту в Rust в безопасном коде проверки есть, и в то же время при использовании функционального стиля компилятору хватает информации, чтобы часто генерировать код без проверок.
Очевидно, что в С++ этот подход уже не сработает, стандартная библиотека такая, какая есть и компиляторы такие, какие есть.
Ещё интереснен подход в Pascal. Там, насколько я помню, генерацию проверок можно было отключать для конкретных участков кода. Это тоже хороший подход, но в C++ кажется так не получится сделать.
S>Почему не работает: S> std::vector<int> vector1 = { 1,2,3,4,5 }; S> std::vector<int> vector2(5); S> std::ranges::copy(vector1 | std::views::drop(11) | std::views::take(100), vector2.begin() + 1);
например потому что copy тут ничего не копирует, не? убери drop(11)
Как много веселых ребят, и все делают велосипед...
S>И вопрос такой. В принципе то C++ не виноват, он же не запрещает проверки выхода за пределы. Однако же виновата философия, которая говорит что такие проверки нужны только школьникам, что настоящие программисты не ошибаются. И я подумал грешным делом — а что если просто добавить обертки для стандартных классов, которые выполняют такие проверки? Не слишком ли смелое решение?
Дело не в школьниках.
дело в эффективности
Проверка индекса при каждом обращении к массиву — офигенно затратное дело...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
O>>например потому что copy тут ничего не копирует, не? убери drop(11) S>Так должно же показать что вышли за пределы по идее?
если ты думаешь что std::drop выходит за пределы, то нет
Как много веселых ребят, и все делают велосипед...
Здравствуйте, Shmj, Вы писали:
S>На мысль навела статья.
В C++ сохранилось множество старинных возможностей, которыми, как правило, следует пренебрегать в пользу эквивалентных фич из «современного С++», если только нет явно причины поступить наоборот (char *, массивы, указатели, malloc/free, NULL, т.д.)
Как-то автор странно обзывает Си.
S>Вот в C++ принято что нет проверки выхода за пределы массива. Т.е. обратились по индексу 6, в то время как элементов всего 6 — вам даже не сообщат об ошибке.
Почитай книгу STL для программистов на C++. Аммерааль Леен. Может быть всё совсем не так плохо как тебе кажется.
S>Как оказалось, дело это не такое уж тривиальное. Вот, к примеру, GPT выдал для проверки c std::ranges — но это не всегда работает как нужно:
Думаю людям нужно читать книги конкретно по алгоритмам, а не пытаться кидать исключения в предполагаемых битомолках. Выход за границу массива это не ошибка языка программирования, это ошибка или не ошибка программиста.
Может быть программист хотел выйти за границу массива, так что ему запрещать теперь. Или пользуйся итератором, тот не позволит выйти за границу массива, если это указать. Да хоть размером массива какая разница.
S>И я подумал грешным делом — а что если просто добавить обертки для стандартных классов, которые выполняют такие проверки? Не слишком ли смелое решение?
Если тебе это так надо, то можешь сделать в режиме отладки, а в релизе использовать нормальную версию. Такую проверку делают макросами в коде, а для замены типа можно использовать typedef.
А вообще, если ты параноик и сам себе не доверяешь, то есть TDD (Разработка через тестирование). Проверяй всё, что делаешь до того, как делаешь.
S>Считается что C++-разработчики не могут допустить таких ошибок и если ты допустил — то вон из профессии, таким как ты тут не место (ну или хотя бы никому не говори об этом).
Здравствуйте, vsb, Вы писали:
vsb>Ещё интереснен подход в Pascal. Там, насколько я помню, генерацию проверок можно было отключать для конкретных участков кода. Это тоже хороший подход, но в C++ кажется так не получится сделать.
Подход интересный, но на практике он не используется. Чистыми массивами пользуются не так то часто, а вот в аналоге vector (TList/TList<T>) эта возможность игнорируется, используется подход плюсов, для кода с проверками и кода без проверок требуется писать разный код
Здравствуйте, vsb, Вы писали:
vsb>проверки будут генерировать медленный код
Не "медленный", а "чуть более медленный". Для подавляющего большинства программ разница не будет заметна без измерений. Но управляться такие проверки должны обязательно.
vsb>компилятор может эти проверки убрать, если докажет, что они избыточны.
Или когда программист явно укажет, что они не нужны. Тогда сразу будет видно — то ли программист достаточо умный (попросил отключить проверки для критичного по времени/объему кода), то ли самонадеянный дурак (попросил отключить глобально, даже в отладочных сборках).
vsb>чтобы: в стандартной библиотеке были проверки
Обращения ко встроенным массивам не имеют никакого отношения к стандартной библиотеке. А библиотеки коммерческих компиляторов как раз имеют встроенные проверки.
, но в то же время типовой код позволял бы компилятору выбрасывать эти проверки, не жертвуя корректностью.
vsb>Очевидно, что в С++ этот подход уже не сработает, стандартная библиотека такая, какая есть и компиляторы такие, какие есть.
Компилияторы постоянно дорабатываются. При этом одни умели вставлять проверки еще в прошлом веке, а другие до сих пор не умеют.
vsb>Ещё интереснен подход в Pascal. Там, насколько я помню, генерацию проверок можно было отключать для конкретных участков кода.
Это возможно в любом языке, в C/C++ для этого используются pragmas. Но вот уровень реализации фантастически, до позора, убог.
vsb>в C++ кажется так не получится сделать.
Это, как раз, одна из наиболее простых в реализации, и одновременно эффективных вещей, которую хорошо бы иметь любому компилятору.