Очередная статья на тему "автор жалуется как ему всё не нравится".В C++ есть вещи, которые меня бесят не только в языке и новых стандартах, но и его компиляторах и системах сборки.
Грехи синтаксиса C++
Точка с запятой;
Вот зачем в Си до сих пор пишут точку с запятой? Существует ли случай, в котором отсутствие ; могло бы вызвать критическую ошибку всей программы? Нет, это придаток доставшийся программистам с древних времён и исбавление от него не вызвало бы никаких проблем - для совместимости старые программы бы просто учитывали точку с запятой, а новые бы понимали что отсутствие символа можно проигноровать. Почему после неймспейсов точка с запятой не нужна, а после структур и классов нужна? Это как-то влияет на выполнение программы? Не влияет ни на что. От фигурных скобочек я тоже был бы рад избавиться, но это уже слишком питон получается да и обратную совместимость было бы сложнее поддерживать чем если бы просто убрали ;
Длинная запись reinterpret_cast<type>(val) и подобных кастов
В старой сишке обходились записью (type)val для преобразования типов, но в плюсах добавили новые касты с защитой от неправильного использования, что хорошо, но почему их запись такая многосимвольная? Можно было бы просто сделать scast, dcast и rcast. Да, сокращения слов до трёх букв считается плохим тоном, но касты в коде встречаются часто и их многократное использование в одной строке очень сильно удлинняет код (у автора нет два широких экрана, ему больновато читать длинный код).
Отсутствие генерации имён
Препроцессор всё время хотят выпилить из C++, но до сих пор не добавили аналога для ## и #, которым можно было бы создавать имена функций из параметров, а так же переводить их в строки и наоборот. Шаблоны тоже могут сделать вам функцию, но выглядеть это будет не красиво. Макросами же можно сделать более компактный код, ну и одновременно более запутанный. К примеру посмотрите сгенерированные макросом функции пиксель-блендинга в ffmpeg там важно вставить функцию смешивания пикселя, на шаблонах бы вам пришлось писать лямбды, которые наверное бы воспринимались как std::function и медленно бы вызывались.
Inline не то, чем кажется
Смысл в инлайне был во встраивании кода функции за место её вызова. Это лишь рекомендация неучитываемая компилятором при релизе и очень даже учитываемая при дебаге, но в C++ inline используют не по прямому назначению. Создатели header-only кода использую это слово для того, чтобы компилятор не ругался на повторное объявление функции. Приписка инлайна ко всему лишь увеличивает код, но это не значит что автор кода хотел чтобы его функции встраивались, он лишь использует инлайн как трюк. Вообще, в одном китайском эмуляторе сеги я видел примерно это:constexpr static inline auto func(args) -> int noexcept const
Я чуть чуть преувеличил конечно, но будьте уверены, в этом стиле выполнены все функции в исходниках этого эмулятора. Понятно же что делает каждое из этих ключевых слов и зачем так было написано, но складывается ощущение, что синтаксис C++ вынуждает программиста делать всё что угодно, только не писать саму программу.
Register
В С++ уже давно убрали это ключевое слово объясняя это как раз тем, что оно часто игнорируется. Кому это мешало? Я например использовал register для индексов в циклах при дебаге. Вас же не интересует как быстро работает сам цикл, вы же хотите минимизировать его скорость работы и замерять скорость того, что в этом цикле.
Реализация компиляторов
Полной реализации того, что понапридумывали в стандартах, мы увидим разве что через 100 лет.
Рандом
Видели мем с картинки? А В MINGW ЭТО НЕ МЕМ!. Реально, до версии 10 std::random_device мог возвращать вам ноль, а мог и вообще не скомпилироваться. Тот кто это придумал видимо был гением, это ведь ещё в C++11 было добавлено, а исправлено только в 2018-м.
std::execution::parallel_policy
С C++17 доступен std::execution::parallel_policy и другие политики выполнения для стандартных функций STL - вы може включать распараллеливания, можете даже УСКОРИТЬ std::sort. В чём проблема? В том что в Mingw вам понадобится отдельно докачать Parallel STL иначе не заработает.
В одном компиляторе есть то, чего нету в другом
Код написанный для разных компиляторов впринципе может быть несовместим, приходится писать разные макросы для обхода проблем. Так же бывает что у одного компилятора есть то, чего нету в стандартной библиотеке, например hash_map. Где-то что-то даже может не собраться, потому что софтина использует какой-то древний вызов сишной функции с какого-то "другого" компилятора.
Медленная сборка
Header-only библиотеки
Использование библиотек написаных внутри одного .h файла считается модерн стилем. Можно вообще написать всю программу на одних хедерах и у вас будет только main.cpp для компиляции. Удобно, никаких лишних повторений кода для описания интерфейсов, можно прописать только один файл для компиляции, но вот время компиляции такой программы может затянуться секунд на 30, что при частой пересборке может оказаться болезненно, а пересобирать код придётся часто, ведь каждый раз компилироваться будет ВСЁ ЗАНОВО. В модульной архитектуре .h/.cpp такой проблемы нет, как и нет проблемы с перекрёстными ссылками. Большинство систем сборки умеют следить за изменениями в коде модулей и собирать только эти изменённые модули не пересобирая всю программу. Многопоточность в этом случае поможет собрать модули быстрее, но в конце всё упрётся в линкер, он быстрее собирать не станет, можно разве что собрать код с -O0. Ситуацию так же спасает сохранение кеша компиляции, но при header-only подходе это не поможет. Даже библиотека stb_image имеет макрос для включения кода с реализацией, чтобы вы могли отдельно заинклудить эту либу в каком-нибудь .cpp файле и один раз там её собрать. Да даже для iostream придумали iosfwd. Ситуацию должны исправить C++20 модули, но в них тоже есть проблема, о которой ниже.
C++20 Modules
Давно бы пора добавить импорты и экспорты модулей в плюсы, учитывая что такие вещи были уже ассемблере для микроконтроллеров PIC16. Уже сейчас вы можете писать модульный код нового поколения, но это только на бумаге. На практике вам нужно собрать код со специальными флагами разными для GCC и Clang, + для них ещё и по умолчанию заданы разные расширения файлов типа .cxx. Можно конечно указать и своё расширение, но почему это не сделали автоматически? GCC и Mingw-w64 уже умеют в модули, но с 11-й версии. Для Clang'а же нужно ещё и прописать карту модулей в отдельном файле, чего мне бы делать не хотелось. Сейчас же вы можете довольствоваться ошибками компиляции и неполным переносом стандартной библиотеки, хотя обладатели MSVC должны уже иметь std.core и подобные вещи для импорта. Кстати, модули нужно собирать в первую очередь, а только потом мейник, к сожалению система сборки Scons такого не может, но может CMake.
Кодировка Windows
В волшебном мире линукса вам int main(argc, argv) может принять аргументы в UTF-8 и даже не поперхнуться, а вот на винде вам понадобится использовать системнозависимый функционал или wmain, которого нету в GCC и Clang (дефайн UNICODE не прокатит и сборка с ресурсом в виде .xml кода не сработает на Win7). Бывает и что в папке с русскими символами возникают проблемы с путями в самой программа, особенно в Windows XP.
Системы сборки
CMake
Рано или поздно вы можете задолбаться собирать ваш большой проект из скриптов на баше или батнике на винде. IDE сами всё разруливают, но бывает что хочется более кроссплатформенный вариант. Чтож, у нас есть CMake, к сожалению его код на вид ужасен, а в билдах сборки опенсорс проектов сам чёрт ногу сломит. Только в CMake у вас есть 101 способ сделать одно и то же действие функциями из разных версий.
SCons
Предоставляет возможность весело и просто писать питоновские скрипты для сборки C++ прог. Умеет в кеш компиляции и разбиение скриптов на модули. К сожалению не понимает C++20 модули и не предоставляет информации о текущей архитектуре, хотя где-то там внутри знает на чём он запускается. Вам самим нужно прописывать кроссплатформенное поведение.
END
Большинство проблем натянуты и мучают лично меня одного, просто захотел выссказаться. Пишите чё думаете и что вам не нравится в C++. Интересно посмотреть реакцию на инакомыслие, более 44 коментов всяко лучше чем 0 как на моей прошлой статье.
Комментарии (111)
Tzimie
27.01.2022 15:52+1но вот время компиляции такой программы может затянуться секунд на 30
Эх, в 87 году компиляция программы в 1200 строк на Паскале занимала 30 минут
AtariSMN82 Автор
27.01.2022 22:30Игры кое-какой фирмы на движке Alien Engine с доработками как раз компилились у меня 30 мин на компе с Core 2 Quad, но это хотя бы один раз надо так потерпеть при полной пересборке. В офисе той фирмы даже поставили сервер компиляции
Izaron
27.01.2022 15:57+2Вообще, в одном китайском эмуляторе сеги я видел примерно это:
constexpr static inline auto func(args) -> int noexcept const
Не читайте до обеда
советских газетисходники китайских эмуляторов.[dcl.consexpr] -
constexpr
-метод неявно являетсяinline
.static inline
бессмысленный и работает как простоstatic
. И самstatic
не нужен, когда естьconstexpr
. (Если этот метод глобальный)Про "второе значение" слова
inline
, про встраивание кода - примерно с 2013 года Clang перестал ставить таким методам флагinlinehint
в своем промежуточном представлении, и сейчас это значение утратилось. У других компиляторов скорее всего так же.NTDLL
27.01.2022 19:35Вообще, в одном китайском эмуляторе сеги я видел примерно это:
constexpr static inline auto func(args) -> int noexcept const
Какой смысл в модификаторах
static inline?
Если подразумевается, что функция вычисляется на стадии компиляции и в сегменте кода (или ещё где либо) её просто нет.
Videoman
27.01.2022 21:42+1Судя по тому, что у функции модификатор const это мембер класса и смысл в static как раз может быть, а вот constexpr и так автоматом делает такую функцию inline. Т.е. как раз inline тут лишний.
AtariSMN82 Автор
27.01.2022 22:34да,
constexpr static inline
сами себя аннигилируют, но там это видимо сделано на всякий случай
Ritan
27.01.2022 16:00+12А В MINGW ЭТО НЕ МЕМ!.
В том что в Mingw вам понадобится отдельно докачать Parallel STL
GCC и Mingw-w64 уже умеют в модули, но с 11-й версии
Где здесь проблема C++?
Бывает и что в папке с русскими символами возникают проблемы с путями в самой программа, особенно в Windows XP.
А MS-DOS 6.22 не поддерживает имена файлов длиннее, чем 8.3. Нам как, уже начинать считать проблемы OS, вышедшей всего-то за 7 лет до Windows XP, как критичные?
но касты в коде встречаются часто и их многократное использование в одной строке очень сильно удлинняет код
В нормальном коде касты встречаются достаточно редко
вам пришлось писать лямбды, которые наверное бы воспринимались как std::function и медленно бы вызывались.
Это "наверное", говорит очень многое
AtariSMN82 Автор
28.01.2022 00:46-5Где здесь проблема C++?
Проблема компилятора
Нам как, уже начинать считать проблемы OS, вышедшей всего-то за 7 лет до Windows XP, как критичные?
На Доске может никто и не сидит но вот ноуты с икспишкой попадаются часто
amarao
27.01.2022 16:15+2Не знаю как в C, а в Rust -
;
- это очень важная часть синтаксиса, она делает возвращаемый тип unit (()
).Т.е.
fn foo() -> i32 {4} и fn foo() -> i32 {4;}
- это две большие разницы, второе даже не скомпилируется. Благодаря тому, что ; разделяет выражения, можно писать блок ({}) в любом месте.Например, так:
let x = {println("hello here"); 4};
MarazmDed
27.01.2022 16:22+20Можно было бы просто сделать scast, dcast и rcast
Офигенно длинные выражения для кастов - не ошибка и не случайность. Это сделано ПРЕДНАМЕРЕННО. Чтобы вызывать чувство дискомфорта. Каст - исключительная конструкция, которой в норме не должно быть. Если увас много кастов - что-то в коде у вас не то. Говнокодом попахивает.
а после структур и классов нужна?
Таки исторически сложилось, что структуры и классы определяют тип. И после определения типа вы можете сразу определить переменную данного типа. Точка с запятой говорит, что вы эту возможность не использовали. Все остальное - обратная совместимость.
Лично меня ни скобки, ни точки с запятой не напрягают.
dmitrmax
27.01.2022 17:02+3Странно, что автор не пожаловался на необязательность выравнивания кода, если вы понимаете, о чем я =)
kamilkamilkamil
28.01.2022 00:37-2Читаю и думаю: прикольно, понятное объяснение.
Потом читаю ник.
Теперь не могу понять маразм это или правда.
AtariSMN82 Автор
28.01.2022 00:57-1Каст - исключительная конструкция
Ну а если надо кастовать данные в байты при работе с файлами или сделать динамик каст с базового класса, разве это плохо?
rat1
28.01.2022 01:20+6Если вам приходится делаеть динамик каст, то вы априори что-то не то надизайнили) Тем более могут быть определенные проблемы с RTTI, в зависимости от компилятора.
allcreater
28.01.2022 17:04+1Да, в IO функциях кастовать не воспрещается, и временами вовсе необходимо: передать массив как массив байт, а так же сделать memcpy(не reinterpret_cast!) при обратном преобразовании массива байт в объекты.
C++20 для обоих случаев даже специальные библиотечные функции получил: std::as_bytes, std::bit_cast.Частое же употребление кастов в прикладном коде - признак плохого дизайна. Лично у меня временами такое тоже случается и тоже подгорает, но тут надо не забывать, что плохой код у меня - мне его и переписывать, разработчики языка в этом не виноваты.
MarazmDed
29.01.2022 17:33+1Ну а если надо кастовать данные в байты при работе с файлами
Это и есть та самая редкая исключительная ситуация.
или сделать динамик каст с базового класса, разве это плохо?
Это - очень плохо. Нарушение буквы L в принципах SOLID. Базовый класс не должен знать ничего про потомков. Как раз здесь и должна сыграть свою роль ужасность конструкции каста.
bfDeveloper
27.01.2022 16:49+13Весь раздел про синтаксис - демонстрация его полного незнания и непонимания. Хоть бы почитали, откуда взялась эта точка с запятой, особенно после классов.
В некоторых других моментах есть зерно истины, но накидали в одну кучу так, что пользы - ноль. Даже крик души полезно структурировать.
AtariSMN82 Автор
28.01.2022 01:14-1Можно было бы понаделать статей на разные крики души, но они сами по себе и так бесполезны и ничего бы не решили. Разрабы компиляторов и сборочных систем наверное не читают хабр. Остаётся только направленно указывать им в пул реквестах свои хотелки и рассказывать о проблемах
dmitrmax
27.01.2022 17:01+2В волшебном мире линукса вам int main(argc, argv) может принять аргументы в UTF-8 и даже не поперхнуться, а вот на винде вам понадобится использовать системнозависимый функционал или wmain, которого нету в GCC и Clang
Участвовал в проекте, где по неосторожности ради разработчиков, пишуших под виндой на старте проекта было принято решение использовать wide character'ы для мультиплатформенности и соответственно std::wstring. В итоге:
1) Теперь всё это дерьмо в Linux (а это таргет платформа) конвертируется из utf-8 в UCS-4, что сжирает порой много времени на работу iconv
2) В винде всё равно используется кастрированный юникод на два байта шириной.
3) Везде приходится писать L"".
И так далее. Иногда это вылезает в самых неожиданных местах.
На мой взгляд идти по пути системы, создатели которой всегда выбирают (или выбирали) альтернативно-одаренный путь чисто на зло всему остальному миру стандартов, это путь испытать на себе весь набор грабель в лоб.
Если вы пишите юзерский софт - закройте эти проблемы библиотекой типа Qt. Если пишете серверный/системный софт - просто забейте на винду.Videoman
27.01.2022 17:46… на старте проекта было принято решение использовать wide character'ы для мультиплатформенности и соответственно std::wstring. В итоге:
А какая связь между UNICODE конфигурацией WinAPI и выбранным вами std::wstring. Что мешало выбрать std::string или std::u8string и использовать UTF-8?dmitrmax
27.01.2022 18:13Что мешало выбрать .... std::u8string ....?
Как минимум 2010 год.
Что мешало выбрать std::string .... и использовать UTF-8?
И много ли полезного можно сделать с UTF-8 под Windows? Напечатать по-русски в консоль? Вывести SetWindowText'ом? Даже в блокноте не посмотреть, если там есть non-ASCII. Я уже не говорю о том, что данные, которые приходилось обрабатывать, надо было конвертировать из архаичных кодировок сначала, наверное, в Windows Unicode, а потом в UTF-8 переконверчивать? Ну и к тому же, это потянуло бы какие-то внешние библиотеки, так как готовых средств по работе с UTF-8 на тот момент в Windows я не помню. Как сейчас не знаю.
Videoman
27.01.2022 18:39+1У меня тоже весь код кроссплатформенный. Использую именно такой подход с 2006 года. Подход следующий, что касается С++: строки std::string в кодировке UTF-8. В платформозависимых местах под Windows происходит конвертация в UTF-16 и обратно в UFT-8, если надо. Всё, никаких проблем за всё это время нет. Никаких этих ваших уродских L"". Никаких проблем в Linux. Чем вам такой подход не нравится?
И если серьёзно подходить, то никакая это не проблема С++. Это проблема неудачно выбранного технического решения. Максимум проблема API на разных OS.dmitrmax
27.01.2022 19:37+3Вообще-то я этот тред и начал для того, чтобы стало понятным, что это не проблема C++.
mayorovp
28.01.2022 11:21Блокнот давно уже поддерживает UTF-8.
В "десятке" это делается вот так:
СкриншотВ более старый версиях некоторых пунктов не было, но поддержка UTF-8 точно была.
dmitrmax
28.01.2022 15:22Коллега, то было в 2010 году, в ходу была Windows 7. Не было там поддержки UTF-8 в блокноте, на сколько я помню.
Videoman
28.01.2022 15:30+1Вот ссылка от Microsoft на тему UTF-8 в Notepad. Обратите внимание на дату: Апрель 2010
Videoman
27.01.2022 17:21+4Почему после неймспейсов точка с запятой не нужна, а после структур и классов нужна?
Потому-что возможна декларация одновременно с объявлением.Можно было бы просто сделать scast, dcast и rcast
Автору стоить почитать предметно почему специально так было сделано и выбраны длинные имена.constexpr static inline auto func(args) -> int noexcept const
А что здесь лишнего по мнению автора?constexpr static int func(args) const noexcept;
Так же бывает что у одного компилятора есть то, чего нету в стандартной библиотеки, например std::hash_map
Что за контейнер такой ??? Автор, вы точно пишете на С++винде вам понадобится использовать системно-зависимый функционал или wmain
Причем тут С++? В windows никто вас не засталяет, пожалуйста, объявляете main как int main(int argc, char* argv[]), но тогда и аргументы и параметры соизвольте передавать в UTF-8.Большинство проблем натянуты и мучают лично меня одного, просто захотел высказаться.
Прям так же и хочется ответить. Полное ощущение что статья стоит из потока мыслей, с надерганными откуда-то случайными примерами, без малейшей попытки разобраться что и как и без понимания какие реальные проблемы есть в С++.0xd34df00d
27.01.2022 22:57+5А что здесь лишнего по мнению автора?
Про автора не знаю, но зачем нужен
constexpr
, я не понимаю до сих пор. Он не входит в тип функции, это по факту всего лишь хинт компилятору на «эту функцию можно попробовать вычислить в константном контексте». Почему все функции не могли бы быть implicitly constexpr, непонятно.А, ну кроме возможности добавить ещё одно UB, конечно, потому что constexpr-функция, не являющаяся constexpr ни для одного набора параметров, делает программу ill-formed/NDR. Но надёжно воспользоваться этим тоже нельзя, потому что
constexpr bool shitfunc(Nat a, Nat b, Nat c, Nat k) { k += 3; if (pow(a, k) + pow(b, k) == pow(c, k)) return true; throw 42; }
потребует от компилятора доказать теорему Ферма.
Зачем это UB добавлено — тоже непонятно. Ну, кроме того, что в Комитете сидят либо напрочь оторванные от практики люди, либо психопаты, ненавидящие программистов. Учитывая, что некоторые из сидящих таки пишут код в рабочее время, это скорее второй вариант.
Videoman
28.01.2022 12:58+2Почему все функции не могли бы быть implicitly constexpr, непонятно.
… в Комитете сидят либо напрочь оторванные от практики люди, либо психопаты, ненавидящие программистов.
Как не парадоксально, но может оказаться совсем наоборот — эту фичу пилили какие-нибудь практики, которые решали свою, сугубо локально задачу, которые были так этим заняты, что у них не было уже сил поднять голову и осмотреться по сторонам. Об этом косвенно свидетельствует наличие проблемы на которую я наткнулся в своём пет-проекте. Вот как можно было в стандарте зафиксировать, что constexpr метод может отработать во время компиляции, а может и не отработать, но при этом не подумать, что бывает что реализации существенно отличаются и не дать инструментов которые дали бы возможность определить в каком режиме сейчас находится код ?! И только в С++20 вдруг очнулись и добавили std::is_constant_evaluated().0xd34df00d
28.01.2022 19:46+2Вот как можно было в стандарте зафиксировать, что constexpr метод может отработать во время компиляции, а может и не отработать, но при этом не подумать, что бывает что реализации существенно отличаются и не дать инструментов которые дали бы возможность определить в каком режиме сейчас находится код ?! И только в С++20 вдруг очнулись и добавили std::is_constant_evaluated().
А вот тут я тоже не соглашусь :]
ИМХО
std::is_constant_evaluated()
— костыль с жутко неинтуитивным поведением. Ситуации, описанные в пропозале, который я читал года три назад, выглядят какими-то уж больно натянутыми, и мне при всей моей любви к компилтайм-упарыванию ни разу не хотелось этой функцией воспользоваться.Если реализации сильно отличаются, то, возможно, им просто не место в одной функции.
Videoman
28.01.2022 20:33+1Я бы сам хотел что бы компилятор всё оптимизировал за меня и сам обо всём догадывался, но ресурсы и память компилятора не бесконечны, к сожалению.
Если реализации сильно отличаются, то, возможно, им просто не место в одной функции.
Речь не идет о том что бы всё помещать в одну функцию конечно, но в любом случае есть точка принятия решения и её не реализовать без std::is_constant_evaluated().ИМХО std::is_constant_evaluated() — костыль с жутко неинтуитивным поведением. Ситуации, описанные в пропозале, который я читал года три назад, выглядят какими-то уж больно натянутыми, и мне при всей моей любви к компилтайм-упарыванию ни разу не хотелось этой функцией воспользоваться.
Предположим у вас есть шаблонная функция, которая вас устраивает во всех ситуация, в том числе в constexpr режиме. Теперь, предположим, что в случае если шаблонный параметр является целым, кратен 4-м байтам и код выполняется в рантайме, вы можете сильно оптимизировать алгоритм на GPU или ещё как-нибудь. Какое ваше предложение, что делать?0xd34df00d
28.01.2022 22:55+2Речь не идет о том что бы всё помещать в одну функцию конечно, но в любом случае есть точка принятия решения и её не реализовать без std::is_constant_evaluated().
Вы её и так знаете статически, конечно же её можно реализовать без
std::is_constant_evaluated()
!Какое ваше предложение, что делать?
Просто написать две разные функции. Я не уверен, что магия с автодетектом контекста вычисления (тем более, что
is_constant_evaluated
немножко не совсем про то) полезна и читаема.Videoman
29.01.2022 20:06+1Просто написать две разные функции.
Функции физически разные, но одна должна являться перегрузкой другой, т.к. они вызываются в одном и том же контексте, в зависимости от шаблонного параметра и режима выполнения (constexpr). Можете привести пример С++ кода который так может без std::is_constant_evaluated?0xd34df00d
30.01.2022 03:43+1Давайте вы приведёте более-менее законченный пример с
std::is_constant_evaluated()
, а я его перепишу без него?Тем более, что, как мы рядом выяснили, оно зависит далеко не только и не столько от режима выполнения.
Videoman
30.01.2022 13:44+1Отлично, согласен! Заодно может быть вы поможете мне решить вполне практическую задачу. Я вам буду премного благодарен. Поехали! Есть класс: long_uint.h: line 43
Класс реализует арифметику над длинными целыми, в том числе во время компиляции. Нам сейчас не важны все операции возьмем один оператор — меньше (<): long_uint.h: line 279template<typename native_t, uint_t size> class long_uint_t<native_t, size>;
Реализация у него в виде шаблона и очень простая: вычитаем из первого аргумента второй и если произошел перенос в старшем бите, то первый аргумент меньше. Само вычитание c переносом реализовано через функцию subb (Subtract with borrow): long_math.h: line 437. Функция subb() полностью платформонезависима и без проблем работает в constexpr выражениях:template<typename native_t, uint_t size> constexpr bool long_uint_t<native_t, size>::operator<(const long_uint_t& that) const noexcept { native_t digit = digits[0]; bool borrow = sub(digit, that.digits[0]); for (uint_t n = 1; n < std::size(digits); ++n) { digit = digits[n]; borrow = subb(digit, that.digits[n], borrow); } return borrow; }
Дальше, на некоторых платформах, например Windows, мы можем выполнять данную операцию намного эффективнее для 32-х битных беззнаковых целых, делаем перегрузку: long_math_msvc.h: line 372template<typename type_t, std::enable_if_t<is_unsigned_v<type_t>, int>> constexpr bool subb(type_t& value1, type_t value2, bool borrow) noexcept { type_t tmp = value1; value1 -= value2; bool borrow_new = value1 > tmp; tmp = value1; value1 -= borrow; borrow_new = borrow_new || (value1 > tmp); return borrow_new; }
inline bool subb(uint32_t& value1, uint32_t value2, bool borrow) noexcept { return _subborrow_u32(static_cast<uint8_t>(borrow), value1, value2, &value1); }
И вот тут у нас проблема, перегрузка как раз и является частным случаем реализации которая невозможна для функций исполняющихся на этапе компиляции. В данном случае, так как запрещено использование интринсиков совместно с constexpr функциями.
Как бы нам тут могла помочь std::is_constant_evaluated(): мы могли бы реализовать две разные функции subb_constexpr() и subb_runtime() и вызывать одну либо другую в зависимости от контекста:template<typename type_t, std::enable_if_t<is_unsigned_v<type_t>, int>> constexpr bool subb(type_t& value1, type_t value2, bool borrow) noexcept { return std::is_constant_evaluated() ? subb_constexpr(value1, value2, borrow) : subb_runtime(value1, value2, borrow); }
Как это сделать без std::is_constant_evaluated() я не знаю. Какие ваши предложения?mayorovp
30.01.2022 14:51Как это делать в С++ я не знаю, но нормальным решением была бы перегрузка по признаку constexpr:
inline bool subb(uint32_t& value1, uint32_t value2, bool borrow) noexcept { return _subborrow_u32(static_cast<uint8_t>(borrow), value1, value2, &value1); } constexpr bool subb(uint32_t& value1, uint32_t value2, bool borrow) noexcept { return subb_constexpr(value1, value2, borrow); }
Videoman
30.01.2022 15:14В том то и проблема, что constexpr не входит в тип функции и по нему не возможна перегрузка. Это не тоже самое что спецификация const в конце метода класса.
technic93
28.01.2022 20:38на сppreference всё логично описано.
0xd34df00d
29.01.2022 01:17+3Не знаю про cppreference, знаю про то, какие эффекты это имеет.
Давайте начнём с простой функции, которая, согласно текущему стандарту, никогда не может быть вызвана в constant evaluated context:
template<bool> struct Detect; template<> struct Detect<true> { using type = int; }; void foo() { Detect<std::is_constant_evaluated()>::type test; }
Соответственно,
Detector<std::is_constant_evaluated()>::type test;
будет валидным выражением, когдаis_constant_evaluated()
возвращаетtrue
, а иначе — ошибкой компиляции. Как думаете, скомпилируется?Скомпилируется! Почему? Потому, что
is_constant_evaluated()
проверяет не то, вызывается ли содержащая её функция в константном контексте, а то, вызывается ли самаis_constant_evaluated()
в константном контексте. В принципе, на этом можно и закончить, но давайте продолжим.bool foo() { return std::is_constant_evaluated(); }
даёт нам
_Z3foov: xor eax, eax ret
Ну, то есть, функция резко стала
false
. Потому, что контекст больше не константный.Продолжаем:
constexpr int foo() { return std::is_constant_evaluated(); } int global = foo();
смотрим дизасм:
global: .long 1 # 0x1
Ну, то есть,
true
. Ок, а так?constexpr int foo() { return std::is_constant_evaluated(); } inline int bar() { return 0; } int global = bar() ? foo() : foo();
Дизасм:
global: .long 0 # 0x0
Теперь оно
false
(что мне на высказанную мной ранее модель натянуть не удалось, к слову). Что, кстати, довольно иронично: компилятор в компилтайме видит, что выражениеfalse
, но оно не является компил-тайм выражением. muh logicОк, а если создать временную переменную? Это — рефакторинг, который не должен иметь никакой семантической разницы для булов, верно?
constexpr int foo() { const bool meh = std::is_constant_evaluated(); return meh; } inline int bar() { return 0; } int global = bar() ? foo() : foo();
Ага, десять раз, теперь это true:
global: .long 1 # 0x1
А, чисто ради лулзов, если вот так?
constexpr int foo() { Detect<std::is_constant_evaluated()>::type test; return std::is_constant_evaluated(); } inline int bar() { return 0; } int global = bar() ? foo() : foo();
Что будет? Единичка в
global
? Ошибка компиляции?Ни то, ни другое! Этот код скомпилируется успешно (то есть, на первой строчке функции там
true
), но вglobal
будет 0 (то есть, на второй строчкеfalse
).Чё-т как-то ну так себе логика.
technic93
29.01.2022 02:46-1Вполне закономерно. Вы везде пытались поменять ответ в зависимости от const_evaluated. А он предназначен для противоположной цели. И там вполне ясно сказано что тестируется выражение содержащие is_const_evaluated() а не функция в которой содержится эта инструкция. Пример с тренарником интереснее, но про него есть в документации.
0xd34df00d
30.01.2022 03:42Вы везде пытались поменять ответ в зависимости от const_evaluated.
Я пытаюсь посмотреть, как он работает, и построить интуицию. Интуиция не очень строится.
А он предназначен для противоположной цели.
Чтобы не менять ответ в зависимости от него? Ну тогда его просто не надо вызывать.
technic93
30.01.2022 13:33Да, ответ менять не надо - надо менять реализацию. Вам хочется поднять его на уровень типов, кажется что такая возможность только добавит непонятных моментов в коде. Тогда ваш рефакторинг а ля вынесли константу в новую переменную не просто поменяет true на false, а может поменять int на float.
crackedmind
30.01.2022 12:26Чтобы исправить этот костыль в С++23 добавили if consteval { } без condition
technic93
28.01.2022 14:49Пробовать вычислить любую функцию в constexpr может быть проблематично, если ее вычисление занимает много времени, или вообще не известно завершима ли она. Что компиляторы должны делать в таком случае? И что они делают сейчас интересно.
Izaron
28.01.2022 15:10+1или вообще не известно завершима ли она. Что компиляторы должны делать в таком случае?
Есть предел по количеству выполнимых команд, после которого компилятор откажется досчитывать метод. Заранее сказать, завершится ли функция, они никогда не смогут из-за проблемы остановки.
qw1
28.01.2022 15:20Ну отлично… Программа не скомпилировалась, потому что компилятор запускался на слабом компьютере ))
technic93
28.01.2022 15:58Но если такой предел пробовать для всех функций, то это займет больше времени, чем только для constexpr. Хотя пропагирование констант работает и для обычных функций часто, без пометки constexpr.
0xd34df00d
28.01.2022 19:47+1Ну, с оптимизациями компилятор и так всё пытается вычислить и упростить, а без оптимизаций надо вычислять только то, что используется в соответствующем контексте. Условно, видим
constexpr auto a = foo(); SomeType<bar()> baz;
— вычисляем. Не видим — не вычисляем.
technic93
28.01.2022 20:27Т.е. он не делает разницы для constexpr и нет функций на этапе оптимизаций?
Izaron
28.01.2022 15:30С октября 2021 года есть пропозал чтобы то, что вы описали, больше не будет ill-formed/NDR
http://open-std.org/JTC1/SC22/WG21/docs/papers/2022/p2448r1.html#pnum_24
AtariSMN82 Автор
28.01.2022 01:29-2почему специально так было сделано
Уж этого я правда не почитал
А что здесь лишнего по мнению автора?
Можно было бы обойтись int func(args), там во всех функциях такой стиль был
Что за контейнер такой ???
Ой да, в std этого нет
Прям так же и хочется ответить. Полное ощущение что статья стоит из потока мыслей, с надерганными откуда-то случайными примерами, без малейшей попытки разобраться что и как и без понимания какие реальные проблемы есть в С++.
Это так и есть
tangro
27.01.2022 19:00+5Боль от С++ у всех своя, кого-то одно тревожит, кого-то другое. Однако пока будут задачи, которые ничем, кроме плюсов не решить, мы будем с этим жить. В куче мест на предложение переписать всё к чертям на шарпе или JS люди улыбаются и крутят пальцем у виска. Поэтому пишите себе точку с запятой и смиритесь с этим.
0xd34df00d
27.01.2022 22:46+6К счастью, кроме шарпа и жс есть и другие языки. Да и задачи, не решаемые ни на чём, кроме плюсов, в основном связаны с наличием уже имеющейся плюсовой базы, а это так себе аргумент.
Playa
27.01.2022 21:55+4Для меня точка с запятой это как минимум якорь, за который цепляются глаза при чтении кода. Сразу видно, что вот, мол, конец выражения.
Автору советую расслабиться и получать удовольствие.
AtariSMN82 Автор
28.01.2022 01:36-4Ок. Особенно радует невозбранное использование любого числа точек с;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
netch80
28.01.2022 00:45+2Набор боли от C++ у автора достаточно мало коррелирует с моим — настолько, что я не понимаю, что именно он пишет и почему. У меня тут следующее:
— Само завершение операторов (statements) знаком ';' нравится больше, чем варианты его автогенерации в конце строки, если нет причин считать, что будет продолжение (как в Go, когда последним стоит операция (operator) типа "+", или как в Python, когда явная '\' или незакрытые скобки).
А вот когда ставится ';' после '}' — тут, да, бардак. Пояснение в соседних комментариях, которое по сути сводится к отличиюstruct moo { int a, b; };
отstruct moo { int a, b; } moo1, moo2;
на самом деле это последствие концептуальной слабости, что сэкономили на выделении типа:struct moo { int a, b; }; moo moo1, moo2;
так в большинстве языков, где синтаксис тщательнее продумали.
Второй аспект тут же — необязательность блоковых скобок в телах после if, while… — многие стили требуют их обязательно ставить, и я это поддерживаю — сильно меньше проблем.
> От фигурных скобочек я тоже был бы рад избавиться
Ну да, форсировать группировку отступами — «это питон» со своими недостатками, которые, IMHO, чуть перевешивают достоинства (основные проблемы для меня в матчинге границ блоков в редакторах/IDE).
> Длинная запись reinterpret_cast(val) и подобных кастов
Согласен.
Но тут бо́льшая диверсия (как во всей системе templates) это использование <> как скобок. Здесь спокойно хватило бы и круглых скобок.
> Отсутствие генерации имён
Вообще надо говорить про то, что макры слабы и неудобны. По-нормальному препроцессор должен иметь свой язык и генерировать какой угодно код. А тут — макры C слабы и тянут в одну сторону, а темплеты C++ сильны, но крайне неудобны и тянут в другую сторону, золотой середины нет.
> Создатели header-only кода юзают это слово для того, чтобы компилятор не ругался на повторное объявление функции.
Не видел такого. В основном это таки по назначению — просьба встроить.
> Я например использовал register для индексов в циклах при дебаге.
С SSA все эти рекомендации типа register действительно теряют смысл. Возможно, и до него.
> Код написанный для разных компиляторов впринципе может быть несовместим, приходится писать разные макросы для обхода проблем.
Очевидно, да. Но это не совсем проблема языка.
Из того, что существенно и что автор не назвал (интересно, почему? с чем он работает?)
— Все undefined behavior (UdB), от переполнения знаковых и до хохм типа malloc это не создание значения<a/>.
— Порядок «тип имя» в объявлениях/определениях и все его последствия с раскруткой объявлений навыворот.
— Восьмеричные литералы.
— Нет именованных конструкторов (надо костылировать).
— Сложные принципы поиска подходящей функции, очень часто неочевидные.
— Integer promotions (давно не имеют смысла в современном мире, с редкими исключениями).
— Отсутствие стандартизации (хотя бы в виде рекомендаций) принципов работы с байтовыми данными (как порядок битовых полей в структурах).
— Грабли с '=' вместо '==' (варианты типа if (take a = b) тут были бы в тему).
Это вразброд и где-то 1/3 от всех возможных жалоб, но, IMO, ближе к реальности (повторюсь — в каком именно практическом домене работает автор? Автор, признавайся!)AtariSMN82 Автор
28.01.2022 01:49в каком именно практическом домене работает автор? Автор, признавайся!)
Автор взялся пилить игрушку на плюсах вот и задолбался от длинных имён.
= и == это даа, я вот однажды подумал что std::find при отсутствии результата вернёт 0 и сделал соответствующую проверку. После того как я уже позабыл об этом, стали появляться непонятные баги с искажением данных, которые долгое время не выстреливали и повявились только при релизной сборке0xd34df00d
28.01.2022 02:26+4std::find
не может вернуть 0, потому что он в общем случае возвращает итератор, а итератор далеко не всегда выглядит как указатель.
Kelbon
28.01.2022 08:42+1Не видел такого. В основном это таки по назначению — просьба встроить.
Это не назначение inline, давным давно inline ТОЛЬКО про линковку
Но тут бо́льшая диверсия (как во всей системе templates) это использование <> как скобок. Здесь спокойно хватило бы и круглых скобок.
Вообще то в языке С++ компайл тайм вещи передаются в < >, а рантайм вещи передаются в ( ), про sizeof не нужно заикаться, т.к. это C
темплеты C++ сильны, но крайне неудобны
Значит вы не умеете ими пользоваться, они крайне удобны
Все undefined behavior (UdB), от переполнения знаковых
Нет никакого другого способа разрешить компилятору хоть что то оптимизировать. Нет более хорошего пути чем введение УБ
Кому нахрен нужны именованные конструкторы? Функций не хватает?
Очень простые принципы нахождения подходящей функции - наиболее подходящая. Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.netch80
28.01.2022 09:37+2> Это не назначение inline, давным давно inline ТОЛЬКО про линковку
Попробуйте почитать стандарт, там интересное:10.1.6(2). A function declaration (11.3.5, 12.2.1, 14.3) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions specified in this section shall still be respected.
(цитируется по N4659)
Тут явно указывается цель, и я не вижу причин верить вам больше чем источнику.
А указания для линковки — логичное следствие массового inline в определениях классов.
> Вообще то в языке С++ компайл тайм вещи передаются в < >, а рантайм вещи передаются в ( ), про sizeof не нужно заикаться, т.к. это C
Про sizeof я ни слова не сказал. Что такое эти «вещи» которые «компайл тайм»?
И какое отношение имеет то, что сейчас «передаются», к тому, что использование <> крайне диверсионно для синтаксиса?
> Значит вы не умеете ими пользоваться, они крайне удобны
Когда подавляющее большинство не умеет, это уже проблемы языка. Если вам «крайне удобны», и вы в состоянии на языке шаблонов аккуратно разобраться с определением нужного через каскады всяких enable_if, или продраться через простыни в 7000 строк сообщений об ошибке при неправильном подключении boost.spirit (реальный случай у меня, я честно подсчитал строки) — ну, вам повезло. В основном же на них плюются и стараются минимально использовать.
> Нет никакого другого способа разрешить компилятору хоть что то оптимизировать. Нет более хорошего пути чем введение УБ
1. >95% кода практически любой программы не требуют оптимизаций, которым нужен именно такой UdB, который не виден из контекста данной строки и двух по соседству.
Места, где это действительно нужно, можно было бы оградить пометками «вот тут мы разрешаем многое, будьте внимательнее».
2. Большинство современных применений UdB в компиляторах — это просто злоупотребление возможностями, которые даёт стандарт, а порой и подкрутка стандарта под себя ради 2% в бенчмарках (как по приведенной мной ссылке).
> Кому нахрен нужны именованные конструкторы? Функций не хватает?
Которые конструируют объект? Да, не хватает.
«Кому нужны» — это вы расскажите, например, тем, кто нарвался, что vector<int> x{5} создаёт 5-элементый набор нулей, а vector<int> x{5L} создаёт 1-элементный набор из числа 5 (ещё и урезав long до int без предупреждения). Это самый банальный вариант, а бывают и интереснее, которые народ неделями ловит. (В данном случае, кстати, на винде с LLP64 проблем не будет — а при переносе на Unix взорвётся. Ещё одна радость.)
(Можно закостылить через фиктивный параметр конструктора, но почему не напрямую через имя?)
> Очень простые принципы нахождения подходящей функции — наиболее подходящая.
Да-да-да. С подстановкой неожиданных типов через неявные конверсии.
> Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.
Зависимость от чужого кода, где уже сотворено чёрт-те что и менять не получится — совершенно типовая ситуация. Странно, что вы с этим, судя по сообщениям, не сталкивались.
0xd34df00d
28.01.2022 11:24+3Значит вы не умеете ими пользоваться, они крайне удобны
Только для тех, кто не пробовал другие средства параметрического полиморфизма или метапрограммирования.
Нет никакого другого способа разрешить компилятору хоть что то оптимизировать. Нет более хорошего пути чем введение УБ
Лол, нет. Языки отлично можно оптимизировать и без UB.
Какой простор для оптимизации, кстати, открывает упомянутое мной выше UB с констекспром?
mayorovp
28.01.2022 11:47Мне тут объяснили в прошлом обсуждении про constexpr, что эта ерунда с constexpr вызвана не возможностями оптимизации, а необходимостью иногда вставлять в код отладочный вывод.
Хотя я бы на месте разработчиков стандарта добавил примитивы для constexpr-отладочного вывода вместо того чтобы разрешать constexpr иногда переставать быть constexpr.
dmitrmax
28.01.2022 15:24struct { int a; int b; } moo;
А как тебе такое, Илон Маск?
netch80
28.01.2022 15:31Да, я потом подумал, что надо бы привести это как пример тоже. Но сами подобные безымянные типы — фича не сильно важная.
И при желании и её можно было бы реализовать без ';' (я как раз этого не хочу, но принципиальная возможность налицо).
wavebvg
28.01.2022 01:51Когда в одной статье написано, что не нравится "Точка с запятой;" и "Медленная сборка", то у меня возникает логический диссонанс. Обыватели тоже цвет записывают скрытых вещей в достоинства/недостатки, но чтобы этот вопрос обсуждать...
AtariSMN82 Автор
28.01.2022 01:53-3Точка с запятой;
Да, это смешно и больше связано с моими фантазиями о каком-нибудь быстром питоне
Медленная сборка
Вот тут бы мне очень хотелось чтобы поскорее добавили работоспособные модули, пришлось бы писать меньше кода, а из-за их модульности можно было бы компилировать в многопотоке
Koval97
28.01.2022 05:50Модули в C++ существуют ещё задолго до появления первого компилятора для него, и называется это раздельная компиляция.
И неуча неспособного выучить даже простойшую точку с запятой никто не спрашивал. Общепринятым стандартом для всех зрелых языков программирования, проверенных десятками лет, оно стало вовсе не закрасивые глаза.
wavebvg
28.01.2022 09:56+2Ну я имел в виду работу лексера, потому что построить однозначную таблицу переходов проще (с единственным разделителем, в нашем случае ";"), а уже по таблице разбивать на лексемы намного быстрее и можно сократить количество проходов. Даже в многопроходном языке (C++, к примеру) явный и однозначный разделитель сильно сокращает количество символов для идентификации лексемы при разборе, что позволяет выполнять параллельно лексический и синтаксический разборы без всяких ухищрений.
Тем более использовать перевод каретки как разделитель выглядит странно и лишает возможности форматировать код так, как хочется (даже несмотря на то, что любая ДОС в терминале выполняет команды по кнопке "ввод").
cr0nk
28.01.2022 05:52+1Когда Страуструпа неустраивал C он написал C++. Предлагаю автору не скромничать и написать свой C+++ с модулями и системами сборки.
AtariSMN82 Автор
28.01.2022 16:18-1Зачем чё-то писать, когда есть язык Brat:
p "hello world"
Функция print в одну букву P, вы только вдумайтесь
Koval97
28.01.2022 06:11+2Перефразируя всемизвестную цитату:
"С++ сожет показаться ужасным языком программирования, если вы не пытаетесь написать что-то серьёзное на чем-то ещё".
Если тебе не нравится язык программирования, то просто не пиши на нем. Пиши на том, что нравится, публикуй кейсы, а не кликбейтные заголовки!
netch80
28.01.2022 09:49+4> «С++ сожет показаться ужасным языком программирования, если вы не пытаетесь написать что-то серьёзное на чем-то ещё».
В результате основная часть «энтерпрайза» ушла много лет назад на Java, C#, математики — только подключают C++ код (а местами даже Fortran) через Python или R, почти все девопс-тулзы написаны на Go, и так далее.
Да, бо́льшая часть причин этого — неумение/нежелание морочиться с ручным управлением памятью (одно из самых неудобных и чреватых скрытыми ошибками), но часть аргументов автора статьи даёт вклад и в эти миграции.dmitrmax
28.01.2022 15:28Энтерпрайз ушел в Java и C# по большей части из-за того, что там стандартная библиотека включает в себя больше нужных вещей. До сих пор в плюсах нету стандартных классов для работы с networking, включая высокоуровневые протоколы, gui и так далее. А в те времена, когда энтерпрайз уходил на Java, в std не было даже thread'ов.
netch80
28.01.2022 15:37> Энтерпрайз ушел в Java и C# по большей части из-за того, что там стандартная библиотека включает в себя больше нужных вещей. До сих пор в плюсах нету стандартных классов для работы с networking, включая высокоуровневые протоколы, gui и так далее.
Ну и сейчас многих вещей нет в базовой поставке Java, но есть популярные библиотеки, которые использует каждый второй (не будем вспоминать log4j, но вот хотя бы Guava библиотеки). Аналогично в Python есть всякие http.client, но все ленятся и используют requests. Тут наверняка быстро возникло бы что-то аналогичное. И для C++ есть много библиотек и фреймворков, и что, кому-то сильно плохо, что Qt не в стандарте?
В общем, сомнительный тезис.
> А в те времена, когда энтерпрайз уходил на Java, в std не было даже thread'ов.
Ну да, не было. Но переносимые библиотеки — врапперы вокруг штатных средств ОС — уже были, а уж под конкретный API мог писать каждый.dmitrmax
28.01.2022 16:35+4По сути, только с распространением github + cmake, использование внешних библиотек стало возможным почти так же легко, как и в python можно зацепить pip install'ом. И что там в Java модно? mawen? А раньше это надо было хорошенечко потрахаться, чтобы собрать либу под все нужные платформы, в рилизе, в дебаге и так далее. Помню openssl собирался перловым скриптом, и под винду надо было поставить перл для этого. И прочие, и прочие "ништяки". А потом выходит новая версия либы и опять всё заново.
В общем по сравнению с тем, чтобы просто взять Java, это было серьезно сложнее.
Ryppka
28.01.2022 09:16+1В старой сишке обходились записью type(val) для преобразования типов
Серьезно?
MixonK
28.01.2022 16:08+1В старой сишке обходились записью type(val) для преобразования типов...
С python перепутали видимо. Должно быть имели в виду:
(type)val
0x4eadac43
29.01.2022 10:31Пара мыслей про боль автора с header-only библиотеками.
Если код header-only библиотеки не меняется часто, то ускорить компиляцию поможет использование precompiled headers. В CMake, например, есть поддержка на уровне таргетов.
Вообще, если в своем проекте создавать header-only библиотеки, затем постоянно что-то в них изменять и жаловаться на долгую компиляцию - сам себе злой буратино. Библиотеки делают для распространения кода, чтобы ими пользовались другие авторы, проекты. В таком сценарии, обычно, обновление библиотеки происходит между релизами. Соответственно, проблему постоянных долгих сборок можно избежать, правильно настроив сборочный процесс.
qw1
29.01.2022 12:32PCH кеширует только стадию разбора деклараций. Если h-файл состоит только из деклараций, то ускорение существенное. Если же в h-файле 10 мегабайт кода, который нужно сгенерить и положить в компилируемый сейчас бинарник, на каждой компиляции этот код будет генерироваться заново.
technic93
29.01.2022 23:56В hpp файлах часто темплейты, и их надо разбирать при инстанциировании с новыми темплейт аргументами, и следовательно компилировать заново.
0x4eadac43
30.01.2022 01:38Я не углублялся в детали конкретных компиляторов, если честно. Я не подразумевал, что PCH заменит компиляцию. Каждый translation unit, в любом случае, будет перекомпилироваться, если файлы, от которых он зависит, будут изменены в процессе. Как я понял, PCH означает, что синтаксический разбор не надо будет заново производить, если не было изменений библиотеки, как минимум.
А вам спасибо, что ткнули меня носом в незнание фактуры. Надо будет посмотреть, как только потребуется, конечно.
AngelOfMors
30.01.2022 00:12Наверно, все от скуки и безделья, надо заняться делом.
Нигде нет идеала, мы ко всему подстраиваемся или меняем религию.
Я думаю об алгоритме и т.д., а не о точке с запятой :D
AtariSMN82 Автор
30.01.2022 00:13Но точка с запятой не влияет на алгоритм, только на C++
AngelOfMors
30.01.2022 06:23Много места в ОЗУ занимает эта точка с запятой, фигурная скобка? Палец устал?
Это как мем, где 500 разных арифмитических выражений без скобок в одном выражении, можно и так писать, но я для ясности буду делать осмысленные скобки.
Я после каждой закрывающей скобки блока (даже если там всего две строчки) делаю комментарий к чему она относится (на автомате) // if, // else, // while и т.д.
Код читабельнее. И ни как не напрегает.
А вот длинное reinterpret_cast. Согласен.
ShadowTheAge
Да.
auto p = a; -b;
auto p = a -b;
auto p = a; (b=5);
auto p = a(b=5);
Я не сишник, уверен сишники сейчас еще десяток примеров приведут.
dmitrmax
Сейчас автор скажет, что разделять надо переводами строк и тогда всё будет ОК =)
AtariSMN82 Автор
Не, автор скажет мол незачем в конце строки ставить. Для разделения a, b или a; b было бы норм
4eyes
netch80
Ну для пустого оператора вполне можно было бы nop завести :)
adeshere
Я тут всего лишь "крокодил мимо", но вот у нас в фортране операторы можно разделять точкой с запятой, но при этом ставить ";" в конце каждой строки совершенно не обязательно:
Конечно, при этом приходится терпеть отдельный символ для переноса (продолжения) оператора на несколько строк, т.к. по умолчанию конец строки= конец оператора. Но писать его обычно приходится гораздо реже.
Кстати, строка "return 1914"
в фортране означает совсем не то, что некоторые могли подумать, а так называемый "альтернативный возврат". А именно, управление будет передано не на следующий после call оператор, а на 1914-ю по счету метку в списке фактических параметров процедуры. Как сказал один классик, "эта штука сильнее, чем GOTO!" (c). И - да, такой оператора call (с соответствующим списком меток) без переноса на следующую строку не напишешь ;-)
P.S. Даже интересно, почему при такой любви к лаконичности в Си-семействе еще никто не попытался "залюбить" символ ";" в конце строчки ;-)
ShadowTheAge
А если хочется наоборот — одну команду на несколько строк?
По моему все-таки в с++ и прочих джавах сделано лучше. Когда непечатаемые символы вроде переноса строки или табуляции (питон) влияют на логику программы с этим становится опаснее работать в обычных текстовых редакторах.
dmitrysvd
Табуляцию не принято использовать в питоне, только пробелы. С ними не возникает проблем из-за разной длины табуляции.
adeshere
А если хочется наоборот — одну команду на несколько строк?
Не знаю, как в других языках, но вот в фортране символ переноса строки (точнее, продолжения оператора на следующую строку) вполне себе печатаемый:
&
. То есть, егонадо написать явно:
Кстати, я сам не знал, что у нас можно разбивать литерал на несколько строк. Всегда писал что-то наподобие вот такого:
Может, это вопрос привычки, но в таком формате мне легче читать и с пробелами как-то проще...
Понятно, что у фортрана в силу древности происхождения набор символов изначально весьма ограничен. Например, логическое И пишется, как Condition1.AND.Condition2, а побитовое - это вообще функция (пишется, как IAND(Int1,Int2); впрочем, если она нужна часто, можно перегрузить оператор). Поэтому найти свободный символ для знака продолжения строки, когда такая идея возникла, было не сложно ;-).
Но даже если в языке все символы уже заняты, кто мешает использовать для знака продолжения оператора двойной символ? Ведь даже в очень-очень просторном литературном фортране строки с продолжением встречаются крайне редко. А в языках, стремящихся к лаконичности, наверное, еще реже? Поэтому чисто теоретически (глядя со стороны) возникает впечатление, что изредка написать дополнительный символ продолжения оператора было бы быстрее и проще, чем ставить точку с запятой в конце каждой строки. Но тем не менее это не принято и такой вариант даже не обсуждается. Если это не секретная информация, было бы интересно узнать - почему?
Bobovor
И прав будет. Всрато писать - ловить всратые ошибки. Play stupid games, win stupid prises.
Написать повсратее это вообще спецолимпиада с/с++. Хотя там можно писатт очень читаемо с нулевой ценой.
Ps сам не против точки и скобочек, с ними как то более явно что ли...
qw1
AtariSMN82 Автор
Похоже что пока в C++ будет возможность писать такой код, он всегда будет останется с точкой с запятой
0x4eadac43
Если пофантазировать
qw1
Но автор хочет не так, он хочет
И вот тут, с его предложением, поломаются старые программы. То есть, предложение не рабочее.0x4eadac43
Так, а вот сейчас я понял, что ничего не понимаю в том, что хотел автор :-) Надо было избавиться в синтаксисе от точки с запятой, или же, надо ее оставить, но так, чтобы ее не было (и как это)?
qw1
Он считает (считал), что если точку с запятой сделать опциональной, то ничего не сломается, т.к. всегда можно понять из контекста, лишняя она или не лишняя. Мой пример показывает случай, когда поведение меняется, и компилятор автоматом не сможет вычислить, пропущена точка с запятой умышленно, или это «новый синтаксис».
StepanStulov
Я зумаю автор имел ввиду точку с запятой вместо переноса строки.
zagayevskiy
del