Comments 63
Гхкм, то есть любая программа, принимающая на вход int, и использующая сложение, не программа на С++? Сильное утверждение :)
Ну, если вы не в курсе, что signed overflow - это undefined behavior в контексте языка C++, то могу дать точное место в стандарте об этом, нужно?
Всегда считал такой вывод несколько курьезным. Возьмём тот же гипотетический пример, что и у вашего оппонента - программа, принимающая на вход два инта, и выполняющая их сложение. Да, можно сказать, что она содержит UB, если она не проверяет входные данные перед сложением, а на вход ей подали, скажем, INT_MAX
и 1. Теперь предположим, что существуют некие внешние по отношению к ней гарантии, что ей на вход будут всегда подаваться только корректные данные. Пока программе на вход подаются корректные данные, UB не происходит (ведь это не compile-time, а runtime concept), следовательно, эта программа "написана на C++", отлично. Теперь предположим, что эти внешние гарантии убрали. Теперь получается, что, "написана ли программа на C++ или нет", зависит не от её кода, а от набора внешних по отношению к ней входных данных.
Теперь получается, что, "написана ли программа на C++ или нет", зависит не от её кода, а от набора внешних по отношению к ней входных данных
Всё верно, гарантии можно обеспечить и неязыковыми инструментами. В том числе посредством приставления охранника с дубинкой к каждому пользователю, чтобы валидировать ввод. Однако, это кажется странным только при доведении до абсурда
А если вам, например, нужен эффективный инкремент знакового счётчика, в этом случае довольно осмысленно завязаться на тот факт, что на современном оборудовании его переполнение займёт десятилетия. Но если вы выждали десятилетия, или запустили программу на инопланетном оборудовании и таки переполнили счётчик, внезапно сделав программу не программой на C++, то... ну, вас в меньшей степени будет волновать вопрос того, написана ли программа на C++
И дополнение. Ваш вывод не совсем верен. Согласно стандарту, написана ли программа на C++, всё же, зависит не совсем от внешних факторов. Как только UB стало неизбежно, программа ретроспективно перестаёт быть программой на C++. Т.е. никогда ею и не была, независимо от внешних условий
Здесь, конечно, можно было бы порассуждать, что конкретно перестало быть программой на C++ — программа в рамках этого запуска, каждая копия этого исполняемого файла или вообще каждая функционально эквивалентная программа в мире, но вряд ли стандарт даст вам ответ на этот вопрос. Программа-то больше и не программа, и стандарт на неё не распространяется
Как только UB стало неизбежно, программа ретроспективно перестаёт быть программой на C++.
Снова сильные утверждения. Ваши философские рассуждения не имеют поддержки в виде стандарта, коий чётко разделяет "ill-formed program" и "undefined beaviour". См. определения 3.12 и 3.32 для первого и 3.30 для второго, соответственно, ссылку на стандарт я привёл ниже.
Вы постоянно путаете саму программу и её исполнение. Если понимать, что под "программой на С++" вы имеете в виду "well-formed program" из стандарта, то вышеприведённое утверждение некорректно. В комментарии о неопределённом поведении специально написано, что "Undefined behavior may be expected <...> when a program uses <...> erroneous data." Неопределённое поведение программы при каком-либо её конкретном исполнении (в силу поданных на вход данных) никак не влияет на то, что при другом исполнении на иных входных данных она ведёт себя корректно. Well/ill-formedness это вообще перпендикулярная конструкция.
И только если понимать под "программой на C++" не только саму программу, но и результат исполнения, то да, при наличии где-то внутри исполнения программы UB стандарт разрешает любой результат (точнее, "observable behavior of the abstract machine") везде, даже формально до этого проблемного куска — что, на мой взгляд, довольно плохо, но мотивацию комитета я тоже могу понять.
Но это вновь не отменяет того, что эта же самая программа, запущенная с другими начальными данными, не вызывающими UB, согласно стандарту обязана отработать правильно с полностью определённым поведением абстрактной машины. Именно поэтому ваше определение "программы на C++" мне кажется нелогичным.
Не совсем. Дело в том, что undefined behaviour возникает именно на уровне исполнения виртуальной машиной, и может возникать даже в well-formed program при некоторых входных данных. Таким образом, само по себе наличие потенциального UB не делает программу ill-formed. Это разжёвано, например, тут https://devblogs.microsoft.com/oldnewthing/20240802-00/?p=110091
Да, давайте :)
Приведите, пожалуйста, место в стандарте, где signed overflow - undefined behavior
Самое грустное что существует тьма либ которыми все пользуются где есть UB и всем всёравно. Многие "старые" библиотеки для математики 3D графики используют union не правильно. Но эти либы стали чуть ли не отраслевым стандартом, особенно таких много для OpenGL.
Тут надо прям полностью базовые инструменты менять, чтоб это всё корректно работало, а это много работы.
Processor<KeyValueStore<std::list<Wrapper<std::string>>, std::list<Wrapper<std::string>>>>;
не спасёт ничего.
С его предложениями, это получается уже не С++. И становится проще на Rust переписать, чем делать мешанину старого и нового кода
Переписать C++ код на Rust существенно сложнее, чем на SafeC++. Хотя бы потому, что не требуется менять архитектуру программы, отказываясь от наследований, которых в Rust просто нет.
Не следует так же забывать о стабильности и стандартизации Rust. В Rust и его std библиотеке свыше 4 тыс. файлов, содержащих unsafe блоки. Причем во многих случаях безопасность unsafe кода зависит от контекста вне этих блоков. Например, в Rust невозможно без unsafe передать функции неинициализированный буфер. И если это делается, то только анализ кода функции покажет, действительно ли она только пишет в этот буфер и ничего из него не читает, кроме ей же и записанного. Где гарантии, что в этих тысячах unsafe блоков меньше критичных ошибок, чем в вылизанных десятилетиями стандартных библиотеках C++? При этом до сих пор существуют возможности даже без unsafe блоков добиться, для примера, use-after-free, пусть и не тривиальным путем. Причем история эта длится уже девять лет!
Так же Rust не контролирует обращение за границы массива в релизной сборке. Так что те же самые C++ ошибки с выходом за границы буфера в нем не исключены. UB в Rust тоже имеют место быть. Для примера, при исчезновении порядка.
Потенциально, Rust действительно может позволить писать более безопасный код, чем C++. Но действительно ли он уже готов для этого? Может все же требуется еще несколько лет для стабилизации кодовой базы и стандартизации?
Ну все, высказался, минусуйте. Но я все равно буду использовать Rust как для хранимых процедур PostgreSQL, так и в качестве хобби для ESP32. Потому что если на нем не кодить, улучшить его и сделать действительно безопасным точно не получится.
Феерический набор клише и недостоверной информации.
Например, в Rust невозможно без unsafe передать функции неинициализированный буфер.
MaybeUninit в стабильной версии компилятора уже более 3 лет.
Где гарантии, что в этих тысячах unsafe блоков меньше критичных ошибок, чем в вылизанных десятилетиями стандартных библиотеках C++?
Внутри может быть сколько угодно unsafe кода, но наружу торчит безопасное API, т.к. инварианты для unsafe соблюдены. В отличие от С++, где на каждый чих нужно лезть в документацию и стандарт на предмет UB.
При этом до сих пор существуют возможности даже без unsafe блоков добиться, для примера, use-after-free, пусть и не тривиальным путем.
Это признано багом и будет исправлено. Причем это единственный такой баг, а сколько таких висит в компиляторах С++ и никогда не будет исправлено, т.к. "это не баг а фича"?
Так же Rust не контролирует обращение за границы массива в релизной сборке.
[profile.release]
overflow-checks = true
Это опция существует ~ 8 лет, но вы продолжайте распространять..
UB в Rust тоже имеют место быть.
Для этого нужно написать unsafe код, и это выглядит сложнее и гораздо многословнее, чем safe код. В отличие от С++, где в std короткие и простые методы вызывают UB, и это сделано специально.
overflow-checks это про переполнение чисел, не буферов. i32::MAX + 1 проверять всякие
Эта конкретная опция по умолчанию выключена. Зато тысячи потенциально-опасных мест по-умолчанию не вызывают UB, в отличие от С++.
P.S. Кстати, я перепутал опцию, о чем чуть ниже написали. Обращение за границы массива всегда вызывает панику. А overflow-checks включает панику при проверке переполнения целых чисел. Но и без нее UB не будет!
А отключить проверку границ можно, как в safe через итераторы, так и в unsafe через unchecked* методы.
Так же Rust не контролирует обращение за границы массива в релизной сборке
Неправда, обращение за границы массива контролируются всегда.
Если же говорить о контроле переполнения целочисленных переменных, то @qwerty19106 тогда верно подметил, что имеется overflow-checks
опция.
Причем история эта длится уже девять лет!
Вы гляньте когда появились плюсы и когда появился первый стандарт. Ну а бага компилятора ожидает завершения рефакторинга, который пофиксит это.
Так же Rust не контролирует обращение за границы массива в релизной сборке
Вот тут не верю. Если оптимизатор увидел, что можно не проверять, то проверки он уберёт, что логично. В остальных случаях он явно сотворит некоторую панику. В последнем еженедельнике кстати отличный кейс про портирование кода на Си и оптимизации, в том числе и про оптимизацию bound checkов.
Насколько мне известно, лифтинг времени жизни единственный источник UB в безопасном коде. Скрафтить аналог в живом коде довольно проблематично ибо компилятор сильно этому противится. Есть ещё какая-то проблема с дедлоками, но это к UB не относится.
Ага, то есть вместо того, чтобы улучшить интероп между плюсами и растом, автор предлагает затащить половину раста в сами плюсы. Выглядит как хитрый план по обеспечению безопасности собственной карьеры - после этого и без того запредельная когнитивная сложность языка подскочит еще сильнее, молодежь не захочет его изучать, заменить дедов будет попросту некому - как с COBOL.
Может кто-нибудь объяснить мне весь этот хайп про небезопасность/безопасность при работе с памятью в С/С++ и Rust? Это просто новомодный тренд какой-то? Или какие-то глобальные изменения в С произошли? Что-то не припомню, чтобы программисты "старой школы" жаловались. А то выходят постоянно статьи на эту тему, а я как будто мимо чего-то интересного прохожу.
Раньше все просто жили с CVE про неправильную работу с памятью. Потом появился Rust и внезапно оказалось, что громаднейший пласт ошибок работы с памятью можно избежать на этапе компиляции. Вот суета и началась. Понятное дело, что Rust и SafeC++ не панацея, но тем не менее.
Раньше тоже жаловались, но нужных инструментов просто напросто не было, кроме стат. анализа, который в этом плане сильно менее надёжный банально из-за специфики C/C++.
Внезапно оказалось, что С++ имеет собственные механизмы безопасного управления памятью, вот только их или не используют и они плохо стыкуются с большим объемом старого легаси кода.
Но тут появляется Rust как панацея, с предложение все переписать на нем "потому что безопасно". Вот только тут проблема в слове переписать. Если переписать все старые исходники на С++ с безопасным управлением памятью, то будет ничуть не хуже, чем на Rust.
Вот только кто за такое переписывание возьмется?
UB в С++ слишком легко скрафтить и без дополнительного анализа даже переписать с плюсов на плюсы будет заметно сложнее - скормил nullptr
в string_view
случайно или сделал ++ на висячем итераторе без проверки или вызовы функций в аргументы прокинул и вот тебе UB прямо из стандартной библиотеки. В некоторых случаях это помогает, но чаще приводит к неприятным багам. Так что оба варианта наверняка будут сопоставимы по сложности, только в случае с плюсами все минусы так и останутся на месте.
Это старый докоп от "фанатиков" rust к С/С++
Около года назад столкнулся с ошибкой в одной Open Source библиотеке на C. У меня было свободное время, хотелось освежить навыки низкоуровнего программирования (в основном, много лет пишу на Python, хотя начинал с C/C++), поэтому склонировал код с GitHub и полез в исходники.
Меня хватило где-то на час попыток понять, где выделяется и освобождается память. Я увидел, что несмотря на какие-то редкие попытки освобождать память в случае выхода из функций по ошибке, в процентах 70% случаев память не освобождается (в начале стоит malloc, в конце free, и при выходе по некоторым ошибкам тоже free, но не по всем).
Попробовал порефакторить, но понял, что это будет титанический труд, почти равный переписыванию всей библиотеки с нуля. И надо быть предельно внимательным, чтобы не допустить все те ошибки, которые допустил оригинальный автор. И забил.
Что касается лично моего отношения к C++: по-моему, давно пора закопать стюардессу. Все эти наслоения стандартов приводят к тому, что открыв наугад почти любой исходник на C++, я не пойму в нём ничего. Хотя немного знаю и даже периодически пописываю на нём.
С любым другим языком, даже который не знаю, скорее всего смогу понять общий смысл. Но не с C++ - он превратился в какой-то набор эзотерических заклинаний, понять который может только человек, пишущий на нём непрерывно лет 10, и внимательно вычитывающий все эти неперевариваемые талмуды стандартов.
Может быть, для высочайшей производительности, вся эта эзотерика неизбежна. Но я не верю, что не существует другого пути.
, в процентах 70% случаев память не освобождается (в начале стоит malloc, в конце free, и при выходе по некоторым ошибкам тоже free, но не по всем).
Пробовали LeakSanitizer?
То есть вы не смогли найти проблему с освобождением памяти в C, а закопать надо C++? Где логика?
Логика в том, что вы не осилили дочитать внимательно даже один небольшой комментарий.
Мой комментарий состоит из двух частей, где я описываю своё отношение к языкам C и С++, и описываю различные претензии к обоим.
Мне не нравятся оба языка, мне они оба кажутся отставшими от времени. Точнее, C++ пытается догнать время, но при этом от не отбрасывает всё 30-40 летнее legacy (да и не сможет никогда), и из-за этого код на нём превращается в нечитаемый набор инкантаций, который способны понять очень немногие (они-то, наверное, довольны таким положением и job security).
Так вам и написали, что ваш технический комментарий про язык С справедлив, тогда как при разговоре про С++ вы вспоминаете стюардессу, хотя у С++ уже давно есть механизмы безопасной работы с памятью (решающие проблемы, как описанная вами для С). Вот только их не хотят использовать (потому что сложно и не хотят разбираться и т.д.). Хотя переписать все на совершенно новом языке будет еще значительно сложнее.
По сути ваши претензии сводятся к тому, что вы знакомы с C++ в лучшем случае на уровне "C с классами", а современный C++ вы не знаете и код на нем не понимаете. С чего вы тогда решили, что он "отставший от времени", если вы не в состоянии понять, что там вообще написано? К вашему сведению, C++ находится на переднем крае прогресса в плане написания эффективного обобщенного кода. Далее, с пониманием кода сколько-нибудь сложнее "Hello, world!" на других "современных языках" типа Rust, Swift или Kotlin у вас будет не меньше проблем, везде нужно "погружаться в тему". А видите ли в C++ это делать "не нужно", т.к. вы его "учили" когда-то в 90-е годы. Что тут забавно, так это то, что с вашими знаниями из 90-х вы все еще можете писать на нем вполне рабочий код, и при этом ругаете его по сути за эту самую возможность.
> по-моему, давно пора закопать стюардессу. Все эти наслоения стандартов приводят к тому, что открыв наугад почти любой исходник на C++
а откуда собственно уверенность в том что rust ждет что то лучше спустя столько же времени и что с ним не произойдет эффект Вазы?
Про C++ мы видим как он справляется с каждое 10летие с новыми "убийцами плюсов", как он развивается и что он постепенно совершенствуется, хотя лично мне не нравятся последние 10 лет развития, когда стандарты бегут сильно вперед компиляторов что по моему мнению очень не здоровая тенденция и всё больше мне начинает нравится C. Но я пока надеюсь скоро это устаканится.
Си вообще лучший язык в плане стабильности, если нужно что то ускорить в любом другом языке можно быть на 100% уверенным что к нему можно написать и подцепить модуль на Cи, что делать с растом в этом случае мне не понятно.
Да, C++ и Cи возможно не лучшие языки особенно в плане простоты освоения, хотя лично я не понимаю что в них сложного по крайней мере на уровне рядового перекладывателя json'ов. Но зато они провереные временем, стабильные, отлично работают в тандеме. Имеют огромное ккомьюнити, поддержку, тулинг, даже с самая главная их боль для новичков почти решена (я про зависимости). И если не заниматься написанием каких то библиотек на шаблонах по типу boost::asio он почти ни чем не отличается от какого нибудь C#.
Вот в этом очень длинном комментарии неплохо описаны проблемы современного С++ https://habr.com/ru/articles/813645/comments/#comment_26815569
От себя добавлю:
pointer provenance, которого нет в стандарте, и неизвестно когда будет. Но его добавили во все компиляторы задним числом, даже для режима С89, и он ломает старый, вылизанный десятилетиями код, и усложняет написание нового.
Неочевидные UB, про которые никто не знает, не ловятся санитайзерами и статическим анализом. Например integer promotion https://godbolt.org/z/GWsaGo
Главная (на мой взгляд) проблема С++ - код с UB написать проще, чем без UB, и он выглядит гораздо короче. В Rust же наоборот.
Или какие-то глобальные изменения в С произошли?
Раньше компиляторы С и С++ реализовывали стандарт, а теперь стандарт переписывают по желанию разработчиков компиляторов.
В Rust с безопасностью гораздо лучше , но есть не очевидное следствие: в типах содержится информация, которую программист на С++ держит в уме и комментариях. Из-за этого большой порог входа и некоторые структуры гораздо сложнее написать (например графы).
pointer provenance, которого нет в стандарте, и неизвестно когда будет. Но его добавили во все компиляторы задним числом, даже для режима С89, и он ломает старый, вылизанный десятилетиями код, и усложняет написание нового.
Pointer provenance нечего делать в стандарте, потому что это implementation detail компилятора. С точки зрения стандарта он "ломает" вещи типа произвольного алиасинга указателей или переиспользования старого указателя после вызова realloc()
- в общем то, что и так запрещено в стандарте давным-давно.
Тогда что он делает в proposal к стандарту?
переиспользования старого указателя после вызова
realloc()
- в общем то, что и так запрещено в стандарте давным-давно.
В С89 это разрешено стандартом, и тем не менее pointer provenance всё ломает. Кстати он ломает не только realloc, просто это самый наглядный и легко воспроизводимый случай.
Тогда что он делает в proposal к стандарту?
Ну даже по вашей ссылке в разделе Wording (т.е. предлагаемые поправки к стандарту) этот термин отсутствует. Стандарт отражает эффекты, наблюдаемые с точки зрения погромиста, а не способы достижения этих эффектов в компиляторе.
В С89 это разрешено стандартом, и тем не менее pointer provenance всё ломает.
В отличие от C99, где было введено понятие "allocated storage" и соответствующий ему лайфтайм ("The lifetime of an allocated object extends from the allocation until the deallocation"), в C89 просто-напросто не было такого storage, цитирую:
3.1.2.4 Storage durations of objects
An object has a storage duration that determines its lifetime. There are two storage durations: static and automatic.
An object declared with external or internal linkage, or with the storage-class specifier static has static storage duration. For such an object, storage is reserved and its stored value is initialized only once, prior to program startup. The object exists and retains its last-stored value throughout the execution of the entire program.
An object declared with no linkage and without the storage-class specifier static has automatic storage duration. Storage is guaranteed to be reserved for a new instance of such an object on each normal entry into the block in which it is declared, or on a jump from outside the block to a label in the block or in an enclosed block. If an initialization is specified for the value stored in the object, it is performed on each normal entry, but not if the block is entered by a jump to a label. Storage for the object is no longer guaranteed to be reserved when execution of the block ends in any way. (Entering an enclosed block suspends but does not end execution of the enclosing block. Calling a function that returns suspends but does not end execution of the block containing the call.) The value of a pointer that referred to an object with automatic storage duration that is no longer guaranteed to be reserved is indeterminate.
Это все варианты storage duration, которые были в C89. Что происходит, если объект создается в динамической памяти, вообще не было по сути толком описано. Большое упущение, штош, первый блин комом.
Кстати он ломает не только realloc
Он не ломает ничего такого, что не было бы уже "сломано" согласно соответствующим стандартам.
То что этот конкретный proposal не добавляет в стандарт понятие pointer provenance, не значит что ничего не меняется. Там 1,5 страницы изменений стандарта.
Моя претензия не к самому pointer provenance, а к тому, что поведение компиляторов меняют на ходу, и оно не соответствует стандарту. А потом, поигравшись, пытаются уже поменять в стандарте. Обычно же экспериментальные вещи включаются ключем компилятора, и по умолчанию они выключены.
Из этого и возникло что "уже все сломано", и proposal как будто пытается починить. Но он лишь фиксирует ситуацию.
Из этого и возникло что "уже все сломано", и proposal как будто пытается починить. Но он лишь фиксирует ситуацию.
Я вот почитал сейчас этот пропозал и то, на что он ссылается, и у меня сложилось другое впечатление. Тут немножко другой стандарт в то время был "последним действующим", но по сути неважно - вот автор жалуется:
N4917 does not, however, implement the user-disambiguation (udi) provision: in saying that a pointer subjected to a round trip conversion “will have its original value”, [expr.reinterpret.cast]/5 erroneously forbids real implementations that produce the same integer value for pointers to an object and one past the object that immediately precedes it in memory.
То есть автор по сути жалуется, что текущий стандарт слишком жесткий в плане отслеживания provenance - мол, только те указатели "настоящие", которые сделаны через конверсию pointer -> integer -> pointer, а "сделать" указатель из произвольного integer, пусть даже и с правильным(!) значением, который после конверсии давал бы валидный указатель на объект или на past-the-end объекта, не допускается. И предлагает послабление:
To implement udi, apply the same angelic nondeterminism by which implicit object creation selects the objects to create: if any pointer value exists that corresponds to the integer and gives the program defined behavior, one such value is the result.
То есть если формально существует хотя бы один валидный указатель, который соответствует некоему integer, даже если этот integer не является, так сказать, отслеживаемым производным от этого указателя, то конверсия возможна и валидна, и этот указатель можно будет использовать. То есть предлагается нечто обратное тому, о чем вы пишете - не "зафиксировать", а наоборот, ослабить существующие требования к provenance в определенной степени в случае pointer - integer conversions, чтобы можно было самому "делать" валидные указатели чисто арифметическими операциями над integer, так сказать.
P.S. Уточню, что самостоятельное "производство" валидных указателей чисто арифметическими операциями над integer формально не "благословлялось" ни в каких предыдущих стандартах. Все делалось либо через арифметику указателей с существующим "реальным" указателем (элементарно отслеживается через provenance), либо через конверсию указатель -> integer -> указатель без какой-либо арифметики (в этом случае стандарт гарантирует сохранение значения указателя).
чтобы программисты "старой школы" жаловались
учитывая, что у программисты старой школы про безопасность толком и не знали и делали только так, чтобы программа работала и не падала, не удивительно что они не жаловались. Хайп за безопасность как мне кажется появился после обнаружения червя stuxnet, который иранскую ядерную программу поломал. Государства конечно и раньше делали сертификацию кода, но многих векторов атаки люди просто не могли видеть. Да и проблема сегфолтов всегда комом поперёк горла стояла у разработчиков. Поэтому примерно с 10 года стали открытый софт стал больше париться за безопасность и ваять околоинструментарий - статические анализаторы, фаззеры, и в конце концов стали изобретать языки, в которых можно было бы писать и иметь гарантии, как если бы код генерировался при помощи формального верификатора (типа Coq).
Раньше не парились видимо из-за того что ущерб был кратно меньше. Вирусы конечно сколько-то там миллиардов в год у пользователей отнимали, но на общем фоне было не критично. Можно кстати историю Митника почитать - сколько он там лет занимался взломом подкреплённым социальной инженерией.
Просто в С++ много камней подводных. У тебя долгое время даже std::array мог создать UB на ровном месте, а ты бы даже этого не заметил.
Тут подоспел мем с "Давайте перепишем вселенную на Rust ", ибо они сделали невозможное, запихали всё что может ломаться в unsafe, а наружу торчит красивое и безопасное API и хомячки не переживают что случится отвал жопы в случайном месте (теперь они пытаются обойти тупые ограничения чтобы написать код похожий на С++)
ЗЫ и притом код компилит не хуже плюсов, что тоже плюс.
Я далек от Раста, конечно, но в моем понимании говнокод можно написать на любом языке, да так - что даже встроенные механизмы не спасут.
Потому задача безопасного ПО это задача разработчика, а не языка. И Раст и С++ хороши, но там и там можно стрельнуть в ногу.
Говнокод само собой можно везде написать, но всегда нужно ещё учитывать человеческий фактор. Некоторые ошибки проще совершить в одном языке и сложнее в другом. Просто программисты на c++ любят писать что ошибки с памятью делают только нубы, а "тру" программисты ошибок не делают и память освобождают. Но ведь это сложно всё время быть начеку, особенно в больших проектах, хоть раз да потеряешь бдительность ошибёшься. И раст пытается устранить некоторые ошибки, которые возникают из-за человеческого фактора, уменьшает ментальную нагрузку на программиста, там где это можно сделать.
(Не являюсь пропагандистом раста в плане "нужно всё на нём переписать", хоть и люблю его, но всё таки некоторые преимущества по сравнению с плюсами у него есть)
Вы не верно поняли, тут больше обсуждаются проблемы безопасности, а не говнокода. Говнокод !== уязвимость.
Учитывая, что Core Guidelines существует уже достаточно длительное время и библиотека для следования этим гайдлайнам (gsl - guideline support library) не сказать чтобы много заадоптила в стандартную библиотеку за последние лет 15, непонятно нафига нужен ещё один документ про безопасные плюсы.
Даже сам Херб Саттер (автор гайдлайнов, gsl и кучки книжек) забил на честные безопасные плюсы и решил сделать адекватную обертку над ними в виде cppfront c нормальным синтаксисом и нормальными дефолтами, который при всем при том имеет полную совместимость с С++ кодом.
Одна из причин сложившейся ситуации - в отчаянном нежелании Комитета отказываться от совместимости с C по умолчанию. Вместо того, чтобы потребовать от программиста явных телодвижений для включения поддержки унаследованных от C конструкций ("сужения" типов, "тихих" небезопасных преобразований вроде C-style и function-style casting и т.п.), они упорно тащат эту поддержку включенной "искаропки". В результате и переучившиеся сишники, и многие "изначальные плюсовики", учившиеся на не слишком хороших примерах, автоматически используют все это, и узнают о потенциальной опасности из случайно прочитанных статей или книг.
Та же хрень и с предупреждениями компилятора. Ситуация, в которой программа со множеством потенциальных проблем и уязвимостей успешно компилируется коммерческими компиляторами - это позорище. Давным-давно пора включать все возможные предупреждения по умолчанию, вместе с режимом "предупреждение есть ошибка", и нехай каждый решает для себя - то ли тупо отключать все, то ли отключать выборочно, то ли приводить код в божеский вид.
Ну и со встроенными проверками тоже полный атас. Например, у компиляторов VC++ уже лет двадцать, как есть встроенные проверки на корректность "усекающих" преобразований, использование неинициализированных переменных, затирание обрамляющих "контрольных зон" вокруг объектов, порчу стека внутри функции и т.п., но все они по умолчанию отключены, кроме примитивной проверки целостности стека. Многие профессионалы, годами работающие с VC++, даже не знают, что он это умеет. Автоматически вставлять проверки индексов массива большинство компиляторов не умеет, а проверки указателей перед разыменованием - по-моему, вообще никто.
В итоге и выходит, что C++ без наворотов из STL/boost по безопасности недалеко ушел от голого C, а грамотно использовать навороты умеют не только лишь все.
C++ без наворотов из STL
как вообще язык можно рассматривать в отрыве от его стандартной библиотеке?
как вообще можно использовать язык, до конца не понимая какие возможности дает его стандартная библиотека?
Элементарно. :)
Уникальность C/C++ именно в том, что их стандартные библиотеки написаны на них самих, и любой может написать их самостоятельно. Исключения буквально единичные (longjmp, _alloc и еще несколько функций).
Поэтому и C, и C++, как языки (набор средств для записи алгоритмов), не только можно, но и нужно рассматривать независимо от их стандартных библиотек. Это дает понимание устройства и возможностей языка, а не только умение записывать алгоритм.
Больше костылей и подпорок этой системе костылей и подпорок.
Опубликован документ Safe C++ для продвижения внедрения безопасного кода на C++ вместо перевода проектов на Rust