Здравствуйте, CreatorCray, Вы писали:
S>>Типы данных, которые ХЗ что из себя представляют. CC>А примеры ты при этом приводишь на int-ах
Я привожу тут совершенно разные примеры. В том числе и такие, которые позволяют мне писать меньше буковок.
CC>Если ты хочешь биться головой о функциональщину то не мучай жопу а просто возьми фукнциональный язык.
C++ поддерживает функциональщину начиная с C++98. А с каждым последующим стандартом использовать ее в C++ становится все проще и проще.
CC>>>А вот какого хрена тут auto нету? S>>Потому что это пример. CC>И при этом в другом примере, где auto нахрен не всралось? ты его туда тем не менее влепил.
Оба примера показывают, что в случае длинных имен типов (что встречается в огромном количестве проектов) синтаксис вида type var_name для меня лично менее удобен, чем синтаксис вида var_name: type.
Ну и развитие современных языков программирования, которым не нужно мимикрировать под Си и C++, показывает, что так удобно не только лишь мне.
Здравствуйте, so5team, Вы писали:
CC>>Но вот нахрена тебе там nodiscard для int? S>Вы лучше покажите ситуацию, когда возвращаемое значение можно безопасно похерить. Как-то в моей практике это редко случается.
Вот тут странное замечание. Я смотрел на примеры из cppreference — они нихрена не убедительны. Например:
// nodiscard( string-literal ) (since C++20):
[[nodiscard("PURE FUN")]] int strategic_value(int x, int y) { return x ^ y; }
int main()
{
strategic_value(4, 2); // compiler may warn on discarding a nodiscard valueauto z = strategic_value(0, 0); // ok: return value is not discardedreturn z;
}
Типа у нас чистая функция, которая что-то считает и возвращает результат. Если мы не смотрим результат, то зачем вызывали? Да, всё верно, но!
1. Но мы всё равно напишем тест, который проверит, что программа работает некорректно, где-то логическая ошибка.
2. Такая ситуация возникает не сама по себе, а в цепочке вычислений. Значит, мы не создадим переменную, которая в этой цепочке где-то дальше используется, а значит и не сможем эту переменную использовать далее по цепочке. То есть у нас не ошибка в месте, где мы не присвоили значение, а всё неверно, такой код тупо не будет даже скомпилирован.
S>Вот здесь да, для возвращаемого значения nodiscard не нужен. В остальных 90+ процентах случаев nodiscard обязателен и то, что в C++ нет nodiscard-а по умолчанию -- это такой нехилых проёсёр, с которым теперь придется жить до скончания веков.
Вот мне интересно, насколько nodiscard полезен в принципе везде, где его можно воткнуть? Субъективно кажется, что очень и очень редко пишется функция, проверить результат которой именно что жизненно необходимо. И чаще всего это происходит в библиотечном, а не прикладном коде. Поэтому разумная стратегия здесь, раз уж сложилась ситуация:
1. Писать nodiscard в библиотеках.
2. Не писать nodiscard в обычном прикладном коде абсолютно везде.
3. Писать nodiscard в прикладном коде, когда это внешний интерфейс или прямо таки чувствуется, что надо это сделать.
Здравствуйте, Nuzhny, Вы писали:
CC>>>Но вот нахрена тебе там nodiscard для int? S>>Вы лучше покажите ситуацию, когда возвращаемое значение можно безопасно похерить. Как-то в моей практике это редко случается.
N>Вот тут странное замечание. Я смотрел на примеры из cppreference — они нихрена не убедительны.
Это проблема примера с cpprefrence, а не nodiscard.
Например, допустим, у стандартных контейнеров есть методы empty и size. Допустим, они не помечены как nodiscard (а они, вроде бы, как раз и не помечены).
И в случае (1), и в случае (2) вы явно делаете что-то не то, раз вызываете метод, значение которого вам не интересно.
Или, например, много ли смысла вот в такой конструкции:
make_unique<std::vector<my_data>>(1024);
Именно без сохранения возвращаемого значения?
Как раз nodiscard позволяет компилятору сразу вам сказать где в коде какая-то фигня. Даже без привлечения дополнительных линтеров/анализаторов.
N>Вот мне интересно, насколько nodiscard полезен в принципе везде, где его можно воткнуть?
Просто берете привычку писать его по умолчанию. В большинстве случаев вы его даже замечать не будете. Но изредка, когда вы в зашоренном состоянии из-за овертайма или из-за незнания тонкостей чужого кода, случайно потеряете возвращаемое значение, которое вам терять нельзя, то компилятор вам сразу на это укажет. И сэкономит вам какое-то время на отладку и поиск проблемы.
К дихотомии библиотечный/прикладной код это отношения не имеет.
Здесь нужна такая же привычка сразу проводить границы, как и в случае const/не-const методов и/или noexcept/не-noexcept функций/методов.
Здравствуйте, so5team, Вы писали:
S>Это проблема примера с cpprefrence, а не nodiscard. S>Например, допустим, у стандартных контейнеров есть методы empty и size. Допустим, они не помечены как nodiscard (а они, вроде бы, как раз и не помечены).
S>Есть ли смысл в программе вызов вроде: S>
S>? S>И в случае (1), и в случае (2) вы явно делаете что-то не то, раз вызываете метод, значение которого вам не интересно.
В целом согласен, но пример этот тоже так себе по убедительности. Вызов empty() обычно случается внутри if или цикла. Вызов size() без присвоения ничем не отличается от примера с cppreference: если мы вызвали и ничему не присвоили, значит, что это ничего не будет участвовать в дальнейшей логике работы программы, что сразу будет заметно программисту или компилятору.
Поэтому и кажется, что nodiscard в целом полезен, но неизвестно насколько.
Например, есть вполне понятная статистика: 70% уязвимостей в С коде вызваны некорректной работой с памятью. Придумывают Rust, который решает данную проблему и устраняет эти 70% ошибок. Стоит ли оно того, чтобы учить новый язык и пересаживать на него отрасль? Кажется, что стоит.
Решения комитета С++ немного другие. Ввели nodiscard, который призывает писать больше и немного раздувать код. Насколько он полезен? Я не понимаю. Возможно, что во время обсуждений какие-то более убедительные аргументы у них есть.
S>Как раз nodiscard позволяет компилятору сразу вам сказать где в коде какая-то фигня. Даже без привлечения дополнительных линтеров/анализаторов.
Но если у нас и так используются анализаторы, то они такие проблемы видят, кажется что PVS много опечаток исправляет.
S>К дихотомии библиотечный/прикладной код это отношения не имеет. S>Здесь нужна такая же привычка сразу проводить границы, как и в случае const/не-const методов и/или noexcept/не-noexcept функций/методов.
Вот это и раздражает в С++, что со временем приходится писать всё больше того, что как будто должно быть изначально. Концепция более строгих требованию по-умолчанию и смягчения их по запросу намного лучше, чем то, что имеем сейчас. И все эти полезные нововведения приносят больше раздражения, чем радости.
Здравствуйте, Nuzhny, Вы писали:
N>Вызов empty() обычно случается внутри if или цикла.
А теперь представьте, что у вас было какое-то выражение с нормальным вызовом empty (вроде v.empty() ? a : b), но в процессе какой-то переделки почему-то остался только empty, без ничего. Компилятор этот вызов пропустит. Но зачем это в коде?
N>Вызов size() без присвоения ничем не отличается от примера с cppreference: если мы вызвали и ничему не присвоили, значит, что это ничего не будет участвовать в дальнейшей логике работы программы, что сразу будет заметно программисту или компилятору.
С чего бы?
N>Поэтому и кажется, что nodiscard в целом полезен, но неизвестно насколько.
Пока не попробуете не узнаете.
N>Но если у нас и так используются анализаторы, то они такие проблемы видят, кажется что PVS много опечаток исправляет.
nodiscard позволяет подобные опечатки обнаруживать без анализаторов. Т.е. проблемы в коде находятся и быстрее, и без привлечения сторонних инструментов.
auto s = vec.size();
for (size_t i = 0; i < s; ++i)
{
//....
}
Ошибка компиляции:
vec.size();
for (size_t i = 0; i < s; ++i)
{
//....
}
N>>Поэтому и кажется, что nodiscard в целом полезен, но неизвестно насколько. S>Пока не попробуете не узнаете.
1. А если попробую, то как мне узнать?
2. Кто-то же уже пробовал, оно в стандарте 7 лет назад появилось. До сих пор нет внятных результатов?
S>nodiscard позволяет подобные опечатки обнаруживать без анализаторов. Т.е. проблемы в коде находятся и быстрее, и без привлечения сторонних инструментов.
Здравствуйте, rFLY, Вы писали:
R>>Не должен он быть лаконичным, он должен обеспечивать хорошую читаемость. FLY>Если что-то можно выразить меньшим набором слов, разве это не скажется на читаемость в лучшую сторону?
Ох, не знаю.
В C++ была такая конструкция — =0. То, что в других языках писалось как abstract. Когда смотришь на корень иерархии, видишь сплошное многословие: abstract... abstract... abstract... То ли дело лаконичный C++.
А потом знаешь, что они придумали? "Уничтожать" другие функции, и чтобы синтаксис был консистентным (т.е. тоже через знак "равно"). И теперь есть такая красота как
X& operator=(const X&) = delete;
Надо больше метиламина знаков равенства!
Вот тут-то и понимаешь, что лучше бы изначально был abstract.
N>auto s = vec.size();
N>for (size_t i = 0; i < s; ++i)
N>{
N>//....
N>}
N>
N>Ошибка компиляции:
А вот так? N>
N>vec.size();
N>for (size_t i = 0; i < vec.size(); ++i)
N>{
N>//....
N>}
N>
Зачем вам первый вызов size()?
N>1. А если попробую, то как мне узнать?
Если за несколько месяцев компилятор ни разу не скажет, что у вас теряется возвращенное значение, значит в ваших проектах от nodiscard выгоды нет.
N>2. Кто-то же уже пробовал, оно в стандарте 7 лет назад появилось. До сих пор нет внятных результатов?
У меня, например, есть. Компилятор время от времени бьет по рукам. Поэтому стараюсь nodiscard писать постоянно. И жаль, что это нужно делать. Лучше было бы лишь иногда вписывать discardable для возвращаемых значений.
Здравствуйте, CreatorCray, Вы писали:
A>>А кто умер-то? Rust, например, взлетел. CC>Ну не то чтоб взлетел. Хайпует, да, но это не взлёт. Язык сам с довольно маразматической идеей всё усложнить и простое сделать сложным.
С идеей, как я понял, сделать нативный код безопаснее за счёт повышенного контроля. Мне она параллельна (без неё проще, с ней безопаснее, и то, и другое норм), мне от ЯП нужна, в первую очередь, эстетика и выразительность.
A>>Нормально ты так назвал стандартную библиотеку — вспомогательная опциональная. CC>Потому что язык это исключительно то, что встроено в компилятор. Всё остальное написано на этом же языке и соответственно не явдяется чем то обязательным и неизменным. CC>Так что да, стандартная библа она вспомогательная и опциональная. Промышленный софт прекрасно пишется без её прямого использования.
Понимаешь, это не надъязыковая идеология. Это идеология конкретно C++. Именно этим он мне и не нравится. Позволяет оправдывать сомнительные качества своей стандартной библиотеки тем, что она изолирована и вообще — необязательна. А потом возникает зоопарк.
А в других языках это часть языка, которой ты не можешь не пользоваться. Как LINQ. И это прекрасно. Везде работа с коллекциями ведётся одинаково. Но это ещё уметь надо стандартные библиотеки писать. Попробуй улучшить LINQ!
A>> если бы он был моим начальником CC>Ты не хочешь чтоб он был твоим начальником.
Наличие плавающего размера — это одна из самых уродливых черт С/С++. Ты хоть раз извлёк из этого пользу? Твою программу перенесли на другую платформу без изменения одной строчки кода, и у юзеров ВДРУГ появился доступ к расширенному диапазону? Так никто не пишет лет тридцать уже. Все типы должны иметь однозначно и строго заданный размер, точка
Если у тебя все типы имеют размер, что делать в редких ситуациях, когда тебе нужно явно указать самый распространённый целый знаковый? Некоторые заставляют писать i32, некоторые дают алиас int. Последнее предпочтительнее, глаза цифрами не мозолить
Здравствуйте, Alekzander, Вы писали:
A>С идеей, как я понял, сделать нативный код безопаснее за счёт повышенного контроля.
Идея эта в сферическом вакууме хороша и благородна
У меня претензии как раз к тому, в какое уродство это выразилось в реализации.
A>Понимаешь, это не надъязыковая идеология. Это идеология конкретно C++.
Нет. С++ в отличие от новомодных языков как раз не диктует тебе как тебе выбирать имена, как расставлять отступы и прочее.
A>А в других языках это часть языка, которой ты не можешь не пользоваться.
И это отвратительно!
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, so5team, Вы писали:
A>>А в других языках это часть языка, которой ты не можешь не пользоваться. Как LINQ. И это прекрасно. S>Полагаю, в задачах жесткого реального времени или в ядре ОС это особенно прекрасно.
Совершенно верно.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, Alekzander, Вы писали:
A>Наличие плавающего размера — это одна из самых уродливых черт С/С++.
И опять тебе в портки насрал кто то другой.
В С++ ты сам решаешь чем тебе пользоваться, тебя никто не заставляет пользоваться "неправильным"
Вот жеж народ пошёл беспомощный, если за ручку не ведут и памперс незаметно не меняют то и шага не могут сделать чтоб не обосраться, на этом же поскользнуться, и набрать полный рот своего же продукта.
A>Все типы должны иметь однозначно и строго заданный размер, точка
Приносят твой код на новую 256битную систему где твои крохотные 32битные типы не поддерживаются железом напрямую и потому компилеру приходится городить кучу лишнего кода и привет.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, so5team, Вы писали:
S>Зачем вам первый вызов size()?
Не смог придумать пример поумнее, когда бы у меня вызов size() завис в воздухе. Такое использование видел в старых проектах, чтобы лишний раз функцию не вызывать.
Здравствуйте, Alekzander, Вы писали:
A>Наличие плавающего размера — это одна из самых уродливых черт С/С++. Ты хоть раз извлёк из этого пользу?
С/С++ это системные языки. Соответственно, дождны быть типы данных, размер которых равен размеру машинного слова. В С/С++ это int, size_t, intptr_t, ptrdiff_t etc. Если ты не понимаешь значимости таких типов, то язык просто не для тебя, работай с другим. Тут без шуток и всякого унижения, без этого в принципе НИКАК, введение таких типов — это попытка сделать безопаснее то, что небезопасно и от чего уходят в других языках.
Потом от check_and_abort_if_error отказались, быстренько прошлись по коду и оставили:
do_some_important_operation();
Тем самым проигнорировали код ошибки, который возвращает do_some_important_operation.
* в результате рефакторинга какая-то функция, которая была void, и которая не требовала внимания к себе (например, бросала исключения при проблемах):
complete_current_operation();
была преобразована и стала возвращать признак своей успешности. Т.е. теперь надо бы контролировать ее:
if(const auto r = complete_current_operation(); !r) {...}
но это не было сделано.
* функция/метод возвращает RAII объект, который следовало бы сохранить. Если этого не сделать, то деструктор объекта тут же откатит изменения. Типа:
std::make_unique<my_data>(...);
объект создался и тут же уничтожится.
В реальности с этим поведением могут быть связаны более сложные случаи. Например, у нас в SO-5 вызов send_periodic возвращает timer_id. Если этот timer_id не сохранить, то периодическое сообщение сразу же будет отменено. Поэтому если у кого-то было в коде:
send_delayed<my_msg>(dest, 500ms, ...);
а потом просто поменяли на:
send_periodic<my_msg>(dest, 500ms, 1500ms, ...);
то это приведет к ошибочному поведению именно из-за того, что возвращаемое значение было потеряно.
Эти ситуации первые, которые сходу вспомнились. Возможно, случалось что-то еще.
Здравствуйте, so5team, Вы писали:
A>>А в других языках это часть языка, которой ты не можешь не пользоваться. Как LINQ. И это прекрасно.
S>Полагаю, в задачах жесткого реального времени или в ядре ОС это особенно прекрасно.
S>
Вы тридцать лет уродуете прекрасный язык общего назначения, оправдывая это тем, что зато на нём можно писать ядра ОС. А ядра как писали на Си, так и пишут. Некоторые к Си добавляют Rust.
Впрочем, это всё равно глупый аргумент. Если стандартная библиотека обязательна к использованию, это не значит, что в ядро надо тащить всё, что в ней есть, от регексов до компилятора. Используй те её части, которые быстры и годятся для использования в ядре. Или что надо? Самому то же самое написать?
Здравствуйте, Alekzander, Вы писали:
S>>Полагаю, в задачах жесткого реального времени или в ядре ОС это особенно прекрасно.
S>>
A>Вы тридцать лет уродуете прекрасный язык общего назначения
Во-первых, не я. Я только использую. К развитию языка или его библиотеки отношения не имею от слова совсем.
Во-вторых, не тридцать, а без малого 40.
В-третьих, с учетом того спектра задач, который решался и решается на C++, это действительно язык "общего назначения". Тогда как какие-нибудь Java или C#, или OCaml -- они оказываются больше языками для бизнес, задач. А не общего назначения.
A>А ядра как писали на Си, так и пишут.
И на C++ писали. CreatorCray не даст соврать.
A>Если стандартная библиотека обязательна к использованию, это не значит
Вы уж определитесь: либо обязательна к использованию, либо нет. Ну или признайте, что вы шизофреник со справкой и тогда ваш взаимоисключающий бред можно будет сходу в /dev/null отправлять.
A>Используй те её части, которые быстры и годятся для использования в ядре.