Comments 53
Си++, созданный в режиме совместимости с С, состоит из необъятного набора идиотских дефолтов и предположений. Компилятор — это как миноискатель. Не надо игнорировать его предупреждения, когда идёшь по минному полю.
Но у меня есть стратегия получше! Не ходите по минному полю даже с миноискателем. Используйте Rust, в котором безопасность кода является обязательной.
Минное поле, небезопасно, переходите на язык Абырвалг.
Типично.
Неинтересно.
Даже в быту вы можете себе сделать очень плохо. Можно, например, развести костёр в середине комнаты и пойти спать.
Разница между "развести костёр в середине комнаты" и "случайно наступить на мину" определяет разницу между мирным и (после)военным временем.
Лямбды с замыканием, которое вызывает use after free — это один из примеров, когда С++ пытается на С натянуть шкуру приличного языка, но получается даже хуже, чем было до.
Эта аналогия обладает одним свойством — "безопасность" при этом выглядит производной от физических (неизменных) свойств. Газы горючи и ничего с этим не сделать.
А в реальности же — Си++ писали люди. Ладно, Си, его писали во времена, когда struct был слаще мёда для людей из ассемблера. Но Си++ писался уже по мотивам боли индустрии, и в этот момент было принято множество нетривиальных решений о дефолтах компилятора, дающих unsound код (выглядит прилично, а soundness нет).
Если бы в С++ были другие дефолты поведения — язык бы от этого медленнее не стал, а безопаснее — стал бы.
таковы были трейдоффы за возможность без модификации и написания разнообразных оберток использовать уже существовавшую огромную массу кода, написанного на C.
О, я обожаю этот вопрос. Действительно вызов Си кода из С++ кода не выглядит очень сложным или страшным, но там все равно целая бездна всяких нюансов ) Обратное — это вообще сплошная боль, чего только стоит extern C и всякий манглинг
и в нем UB вполне себе достижим даже в safe коде
А можно про это поподробнее?
Почитайте про флаг -Zsaturating-float-casts и про историю его появления.
Так они же это исправили?
Ну, sort of — через добавление костыля в виде опционального флага
Э… ну насколько я знаком с разработкой rustc это не так. Флаги начинающиеся -Z
это так называемые фича флаги, они работают только в альфа/ночном релизе и служат для тестирования новых идей. В данном случае насколько исправление замедляет код тестеров. При запуске со стабильной версией компилятор просто скажет или выругается или просто проигнорирует '-Z" опции.
в ряде сценариев работы с числами с плавающей точкой заруливается в минуса
Увидел только один вариант который замедлился i128 -> f32, но и его исправили:
https://github.com/rust-lang/rust/pull/67328
play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5c295dd26d187d13c059125bb604c40a
В целочисленных переменных после конверсии по-прежнему произвольный мусор, к тому же разный в debug и release.
P.S. Ссылка про i128 -> f32 не имеет к этой проблеме никакого отношения. Вот ссылка на соответствующий issue:
github.com/rust-lang/rust/issues/10184
Как видите, она до сих пор open (уже больше 6 лет), и в ней написано про negative performance impact от saturating float casts. Судя по всему, решения без performance impact до сих пор нет.
-Wall -Wextra
конечно обязательны. Полезно было прочесть про другие флаги и рекомендации.Уточнение:
-Woverloaded-virtualПереопределение виртуальной функции базового класса в классе-наследнике как раз нормально — она для этого, собственно, и предназначена, а вот перегрузка — это да, скорее всего ошибка.
Предупреждает о попытке в классе-наследнике переопределить виртуальную функцию базового класса
int a = 0;
if (cond)
a = 2;
else
a = 1;
получаем предупреждение: что-то на тему «присваивание значения, которое не используется».В реальном коде бывает и не такое.
Лучше по какому критерию?
Я против инициализации по-умолчанию в очевидных местах (когда значение переменной задается чуть ниже объявления).
if(нажата кнопка) a=1;
else a=2;
Каким значением инициализировать переменную a, нулем? Я вот когда вижу проинициализированную переменную сразу начинаю беспокоится: что её текущее значение означает для системы? Возвращаясь к примеру, может же так быть, что 0 — кодирует факт двойного нажатия на кнопку? А в случае двойного нажатия система выключается… Это ведь совсем не то, что я хотел.
А когда переменная не инициализирована — я спокоен, что инициализирую её ниже корректным значением в зависимости от условий.
Ещё есть любопытный ключик -Weffc++
, который ссылается на книжку "Эффективный и современный С++". Его правила тоже было бы не плохо хотя бы перечислить.
Офигенный ключ, но, к сожалению, в нём есть неотключаемое правило, которое вообще не даёт создать невиртуальный деструктор. А это слишком сильное требование.
Но ради интереса можно временно включать, да.
Разве нельзя данное конкретное предупреждение подавить через pragma disable?
Я такого способа не знаю. Если знаете, покажите, пожалуйста, буду благодарен.
Можно заигнорить как "-W...", так и определённые файлы/строки. А вот валится ли правило про невиртуальный деструктор в общую кучу -Weffc++ или у него есть собственный ключ, чтобы не мучаться с исключением по строкам — не знаю. Вообще этот ключ не советуют использовать из-за размытости, а с -Werror с ним можно застрелиться.
Это всё понятно. Мы же говорим о том, как исключить из -Weffc++
одну конкретную диагностику. Отдельного ключа у неё, естественно, нет.
И удивительно, что очень малая их часть включена по умолчанию
А ничего удивительного — сначала находятся уникумы, компилирующие с «treat warnings as errors», потом выходит новая версия с новыми предупреждениями, потом багтрекер компилятора заваливают репортами «ааааа вы сломали нам билд», потом разработчики компилятора решают, что ну его нафиг и делают новые предупреждения выключенными по умолчанию.
Коротче, те же яйца что и с ABI, только в профиль.
Прошу помощи у сообщества!
Можно ли защититься от случая, когда в одном файле
char arr[1];
а в другом extern char arr[999];
? Компилятор же должен видеть, что arr это и есть arr, почему его не смущает разный их размер?
А С++17 можно делать inline вместо extern, и объявлять таким образом один раз только в хедере. Так же в С++ имеет смысл использовать std::array.
А вообще компилятор ругается когда extern определение не совпадает с обычным. Видимо вы не везде хедер заинклюдили.
technic93 прошу прошения, сразу не отметил. Язык — только Си.
Может быть izvolov или 0xd34df00d знает?
По поводу -Woverloaded-virtual. По совету этой статьи решил попробовать поиспользовать этот флаг. И внезапно столкнулся с проблемой в большом проекте, использующего boost.
Возьмем вот такой код. Как мы видим, он компилируется без варнов. А теперь создадим идентичный код (прототипы методов срисованы с бустовых один-в-один). И внезапно, компилятор начинает говорить о shadow параметра. Что это? Баг компилятора GCC? Потому что Clang, например, не дает варна в таком случае.
Помоги компилятору помочь тебе