Очередная статья на тему "автор жалуется как ему всё не нравится".В 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.
В волшебном мире линукса вам int main(argc, argv) может принять аргументы в UTF-8 и даже не поперхнуться, а вот на винде вам понадобится использовать системнозависимый функционал или wmain, которого нету в GCC и Clang (дефайн UNICODE не прокатит и сборка с ресурсом в виде .xml кода не сработает на Win7). Бывает и что в папке с русскими символами возникают проблемы с путями в самой программа, особенно в Windows XP.
CMake
Рано или поздно вы можете задолбаться собирать ваш большой проект из скриптов на баше или батнике на винде. IDE сами всё разруливают, но бывает что хочется более кроссплатформенный вариант. Чтож, у нас есть CMake, к сожалению его код на вид ужасен, а в билдах сборки опенсорс проектов сам чёрт ногу сломит. Только в CMake у вас есть 101 способ сделать одно и то же действие функциями из разных версий.
SCons
Предоставляет возможность весело и просто писать питоновские скрипты для сборки C++ прог. Умеет в кеш компиляции и разбиение скриптов на модули. К сожалению не понимает C++20 модули и не предоставляет информации о текущей архитектуре, хотя где-то там внутри знает на чём он запускается. Вам самим нужно прописывать кроссплатформенное поведение.
Большинство проблем натянуты и мучают лично меня одного, просто захотел выссказаться. Пишите чё думаете и что вам не нравится в C++. Интересно посмотреть реакцию на инакомыслие, более 44 коментов всяко лучше чем 0 как на моей прошлой статье.
Да.
auto p = a; -b;
auto p = a -b;
auto p = a; (b=5);
auto p = a(b=5);
Я не сишник, уверен сишники сейчас еще десяток примеров приведут.
Сейчас автор скажет, что разделять надо переводами строк и тогда всё будет ОК =)
Не, автор скажет мол незачем в конце строки ставить. Для разделения a, b или a; b было бы норм
Ну для пустого оператора вполне можно было бы nop завести :)
Я тут всего лишь "крокодил мимо", но вот у нас в фортране операторы можно разделять точкой с запятой, но при этом ставить ";" в конце каждой строки совершенно не обязательно:
Конечно, при этом приходится терпеть отдельный символ для переноса (продолжения) оператора на несколько строк, т.к. по умолчанию конец строки= конец оператора. Но писать его обычно приходится гораздо реже.
Кстати, строка "return 1914"
в фортране означает совсем не то, что некоторые могли подумать, а так называемый "альтернативный возврат". А именно, управление будет передано не на следующий после call оператор, а на 1914-ю по счету метку в списке фактических параметров процедуры. Как сказал один классик, "эта штука сильнее, чем GOTO!" (c). И - да, такой оператора call (с соответствующим списком меток) без переноса на следующую строку не напишешь ;-)
P.S. Даже интересно, почему при такой любви к лаконичности в Си-семействе еще никто не попытался "залюбить" символ ";" в конце строчки ;-)
А если хочется наоборот — одну команду на несколько строк?
По моему все-таки в с++ и прочих джавах сделано лучше. Когда непечатаемые символы вроде переноса строки или табуляции (питон) влияют на логику программы с этим становится опаснее работать в обычных текстовых редакторах.
Табуляцию не принято использовать в питоне, только пробелы. С ними не возникает проблем из-за разной длины табуляции.
А если хочется наоборот — одну команду на несколько строк?
Не знаю, как в других языках, но вот в фортране символ переноса строки (точнее, продолжения оператора на следующую строку) вполне себе печатаемый:
&
. То есть, егонадо написать явно:
Кстати, я сам не знал, что у нас можно разбивать литерал на несколько строк. Всегда писал что-то наподобие вот такого:
Может, это вопрос привычки, но в таком формате мне легче читать и с пробелами как-то проще...
Понятно, что у фортрана в силу древности происхождения набор символов изначально весьма ограничен. Например, логическое И пишется, как Condition1.AND.Condition2, а побитовое - это вообще функция (пишется, как IAND(Int1,Int2); впрочем, если она нужна часто, можно перегрузить оператор). Поэтому найти свободный символ для знака продолжения строки, когда такая идея возникла, было не сложно ;-).
Но даже если в языке все символы уже заняты, кто мешает использовать для знака продолжения оператора двойной символ? Ведь даже в очень-очень просторном литературном фортране строки с продолжением встречаются крайне редко. А в языках, стремящихся к лаконичности, наверное, еще реже? Поэтому чисто теоретически (глядя со стороны) возникает впечатление, что изредка написать дополнительный символ продолжения оператора было бы быстрее и проще, чем ставить точку с запятой в конце каждой строки. Но тем не менее это не принято и такой вариант даже не обсуждается. Если это не секретная информация, было бы интересно узнать - почему?
И прав будет. Всрато писать - ловить всратые ошибки. Play stupid games, win stupid prises.
Написать повсратее это вообще спецолимпиада с/с++. Хотя там можно писатт очень читаемо с нулевой ценой.
Ps сам не против точки и скобочек, с ними как то более явно что ли...
Похоже что пока в C++ будет возможность писать такой код, он всегда будет останется с точкой с запятой
Если пофантазировать
Но автор хочет не так, он хочет
И вот тут, с его предложением, поломаются старые программы. То есть, предложение не рабочее.Так, а вот сейчас я понял, что ничего не понимаю в том, что хотел автор :-) Надо было избавиться в синтаксисе от точки с запятой, или же, надо ее оставить, но так, чтобы ее не было (и как это)?
Он считает (считал), что если точку с запятой сделать опциональной, то ничего не сломается, т.к. всегда можно понять из контекста, лишняя она или не лишняя. Мой пример показывает случай, когда поведение меняется, и компилятор автоматом не сможет вычислить, пропущена точка с запятой умышленно, или это «новый синтаксис».
Я зумаю автор имел ввиду точку с запятой вместо переноса строки.
del
Эх, в 87 году компиляция программы в 1200 строк на Паскале занимала 30 минут
Игры кое-какой фирмы на движке Alien Engine с доработками как раз компилились у меня 30 мин на компе с Core 2 Quad, но это хотя бы один раз надо так потерпеть при полной пересборке. В офисе той фирмы даже поставили сервер компиляции
Не читайте до обеда
советских газетисходники китайских эмуляторов.[dcl.consexpr] -
constexpr
-метод неявно являетсяinline
.static inline
бессмысленный и работает как простоstatic
. И самstatic
не нужен, когда естьconstexpr
. (Если этот метод глобальный)Про "второе значение" слова
inline
, про встраивание кода - примерно с 2013 года Clang перестал ставить таким методам флагinlinehint
в своем промежуточном представлении, и сейчас это значение утратилось. У других компиляторов скорее всего так же.Какой смысл в модификаторах
static inline?
Если подразумевается, что функция вычисляется на стадии компиляции и в сегменте кода (или ещё где либо) её просто нет.
Судя по тому, что у функции модификатор const это мембер класса и смысл в static как раз может быть, а вот constexpr и так автоматом делает такую функцию inline. Т.е. как раз inline тут лишний.
да,
constexpr static inline
сами себя аннигилируют, но там это видимо сделано на всякий случайГде здесь проблема C++?
А MS-DOS 6.22 не поддерживает имена файлов длиннее, чем 8.3. Нам как, уже начинать считать проблемы OS, вышедшей всего-то за 7 лет до Windows XP, как критичные?
В нормальном коде касты встречаются достаточно редко
Это "наверное", говорит очень многое
Проблема компилятора
На Доске может никто и не сидит но вот ноуты с икспишкой попадаются часто
Не знаю как в C, а в Rust -
;
- это очень важная часть синтаксиса, она делает возвращаемый тип unit (()
).Т.е.
fn foo() -> i32 {4} и fn foo() -> i32 {4;}
- это две большие разницы, второе даже не скомпилируется. Благодаря тому, что ; разделяет выражения, можно писать блок ({}) в любом месте.Например, так:
let x = {println("hello here"); 4};
Офигенно длинные выражения для кастов - не ошибка и не случайность. Это сделано ПРЕДНАМЕРЕННО. Чтобы вызывать чувство дискомфорта. Каст - исключительная конструкция, которой в норме не должно быть. Если увас много кастов - что-то в коде у вас не то. Говнокодом попахивает.
Таки исторически сложилось, что структуры и классы определяют тип. И после определения типа вы можете сразу определить переменную данного типа. Точка с запятой говорит, что вы эту возможность не использовали. Все остальное - обратная совместимость.
Лично меня ни скобки, ни точки с запятой не напрягают.
Странно, что автор не пожаловался на необязательность выравнивания кода, если вы понимаете, о чем я =)
Читаю и думаю: прикольно, понятное объяснение.
Потом читаю ник.
Теперь не могу понять маразм это или правда.
Ну а если надо кастовать данные в байты при работе с файлами или сделать динамик каст с базового класса, разве это плохо?
Если вам приходится делаеть динамик каст, то вы априори что-то не то надизайнили) Тем более могут быть определенные проблемы с RTTI, в зависимости от компилятора.
Да, в IO функциях кастовать не воспрещается, и временами вовсе необходимо: передать массив как массив байт, а так же сделать memcpy(не reinterpret_cast!) при обратном преобразовании массива байт в объекты.
C++20 для обоих случаев даже специальные библиотечные функции получил: std::as_bytes, std::bit_cast.
Частое же употребление кастов в прикладном коде - признак плохого дизайна. Лично у меня временами такое тоже случается и тоже подгорает, но тут надо не забывать, что плохой код у меня - мне его и переписывать, разработчики языка в этом не виноваты.
Это и есть та самая редкая исключительная ситуация.
Это - очень плохо. Нарушение буквы L в принципах SOLID. Базовый класс не должен знать ничего про потомков. Как раз здесь и должна сыграть свою роль ужасность конструкции каста.
Весь раздел про синтаксис - демонстрация его полного незнания и непонимания. Хоть бы почитали, откуда взялась эта точка с запятой, особенно после классов.
В некоторых других моментах есть зерно истины, но накидали в одну кучу так, что пользы - ноль. Даже крик души полезно структурировать.
Можно было бы понаделать статей на разные крики души, но они сами по себе и так бесполезны и ничего бы не решили. Разрабы компиляторов и сборочных систем наверное не читают хабр. Остаётся только направленно указывать им в пул реквестах свои хотелки и рассказывать о проблемах
Участвовал в проекте, где по неосторожности ради разработчиков, пишуших под виндой на старте проекта было принято решение использовать wide character'ы для мультиплатформенности и соответственно std::wstring. В итоге:
1) Теперь всё это дерьмо в Linux (а это таргет платформа) конвертируется из utf-8 в UCS-4, что сжирает порой много времени на работу iconv
2) В винде всё равно используется кастрированный юникод на два байта шириной.
3) Везде приходится писать L"".
И так далее. Иногда это вылезает в самых неожиданных местах.
На мой взгляд идти по пути системы, создатели которой всегда выбирают (или выбирали) альтернативно-одаренный путь чисто на зло всему остальному миру стандартов, это путь испытать на себе весь набор грабель в лоб.
Если вы пишите юзерский софт - закройте эти проблемы библиотекой типа Qt. Если пишете серверный/системный софт - просто забейте на винду.
Как минимум 2010 год.
И много ли полезного можно сделать с UTF-8 под Windows? Напечатать по-русски в консоль? Вывести SetWindowText'ом? Даже в блокноте не посмотреть, если там есть non-ASCII. Я уже не говорю о том, что данные, которые приходилось обрабатывать, надо было конвертировать из архаичных кодировок сначала, наверное, в Windows Unicode, а потом в UTF-8 переконверчивать? Ну и к тому же, это потянуло бы какие-то внешние библиотеки, так как готовых средств по работе с UTF-8 на тот момент в Windows я не помню. Как сейчас не знаю.
У меня тоже весь код кроссплатформенный. Использую именно такой подход с 2006 года. Подход следующий, что касается С++: строки std::string в кодировке UTF-8. В платформозависимых местах под Windows происходит конвертация в UTF-16 и обратно в UFT-8, если надо. Всё, никаких проблем за всё это время нет. Никаких этих ваших уродских L"". Никаких проблем в Linux. Чем вам такой подход не нравится?
И если серьёзно подходить, то никакая это не проблема С++. Это проблема неудачно выбранного технического решения. Максимум проблема API на разных OS.
Вообще-то я этот тред и начал для того, чтобы стало понятным, что это не проблема C++.
Блокнот давно уже поддерживает UTF-8.
В "десятке" это делается вот так:
В более старый версиях некоторых пунктов не было, но поддержка UTF-8 точно была.
Коллега, то было в 2010 году, в ходу была Windows 7. Не было там поддержки UTF-8 в блокноте, на сколько я помню.
Вот ссылка от Microsoft на тему UTF-8 в Notepad. Обратите внимание на дату: Апрель 2010
Лучше тогда сразу за место wstring брать u8string и u8"Строки"
Подучите сначала современный С++, а потом критикуйте.
Полностью согласен. Как говорится, сказал как отрезал, ни добавить, ни убавить.
Прям так же и хочется ответить. Полное ощущение что статья стоит из потока мыслей, с надерганными откуда-то случайными примерами, без малейшей попытки разобраться что и как и без понимания какие реальные проблемы есть в С++.
Про автора не знаю, но зачем нужен
constexpr
, я не понимаю до сих пор. Он не входит в тип функции, это по факту всего лишь хинт компилятору на «эту функцию можно попробовать вычислить в константном контексте». Почему все функции не могли бы быть implicitly constexpr, непонятно.А, ну кроме возможности добавить ещё одно UB, конечно, потому что constexpr-функция, не являющаяся constexpr ни для одного набора параметров, делает программу ill-formed/NDR. Но надёжно воспользоваться этим тоже нельзя, потому что
потребует от компилятора доказать теорему Ферма.
Зачем это UB добавлено — тоже непонятно. Ну, кроме того, что в Комитете сидят либо напрочь оторванные от практики люди, либо психопаты, ненавидящие программистов. Учитывая, что некоторые из сидящих таки пишут код в рабочее время, это скорее второй вариант.
А вот тут я тоже не соглашусь :]
ИМХО
std::is_constant_evaluated()
— костыль с жутко неинтуитивным поведением. Ситуации, описанные в пропозале, который я читал года три назад, выглядят какими-то уж больно натянутыми, и мне при всей моей любви к компилтайм-упарыванию ни разу не хотелось этой функцией воспользоваться.Если реализации сильно отличаются, то, возможно, им просто не место в одной функции.
Я бы сам хотел что бы компилятор всё оптимизировал за меня и сам обо всём догадывался, но ресурсы и память компилятора не бесконечны, к сожалению.
Речь не идет о том что бы всё помещать в одну функцию конечно, но в любом случае есть точка принятия решения и её не реализовать без std::is_constant_evaluated(). Предположим у вас есть шаблонная функция, которая вас устраивает во всех ситуация, в том числе в constexpr режиме. Теперь, предположим, что в случае если шаблонный параметр является целым, кратен 4-м байтам и код выполняется в рантайме, вы можете сильно оптимизировать алгоритм на GPU или ещё как-нибудь. Какое ваше предложение, что делать?Вы её и так знаете статически, конечно же её можно реализовать без
std::is_constant_evaluated()
!Просто написать две разные функции. Я не уверен, что магия с автодетектом контекста вычисления (тем более, что
is_constant_evaluated
немножко не совсем про то) полезна и читаема.Давайте вы приведёте более-менее законченный пример с
std::is_constant_evaluated()
, а я его перепишу без него?Тем более, что, как мы рядом выяснили, оно зависит далеко не только и не столько от режима выполнения.
Отлично, согласен! Заодно может быть вы поможете мне решить вполне практическую задачу. Я вам буду премного благодарен. Поехали! Есть класс: long_uint.h: line 43
Класс реализует арифметику над длинными целыми, в том числе во время компиляции. Нам сейчас не важны все операции возьмем один оператор — меньше (<): long_uint.h: line 279 Реализация у него в виде шаблона и очень простая: вычитаем из первого аргумента второй и если произошел перенос в старшем бите, то первый аргумент меньше. Само вычитание c переносом реализовано через функцию subb (Subtract with borrow): long_math.h: line 437. Функция subb() полностью платформонезависима и без проблем работает в constexpr выражениях: Дальше, на некоторых платформах, например Windows, мы можем выполнять данную операцию намного эффективнее для 32-х битных беззнаковых целых, делаем перегрузку: long_math_msvc.h: line 372И вот тут у нас проблема, перегрузка как раз и является частным случаем реализации которая невозможна для функций исполняющихся на этапе компиляции. В данном случае, так как запрещено использование интринсиков совместно с constexpr функциями.
Как бы нам тут могла помочь std::is_constant_evaluated(): мы могли бы реализовать две разные функции subb_constexpr() и subb_runtime() и вызывать одну либо другую в зависимости от контекста:
Как это сделать без std::is_constant_evaluated() я не знаю. Какие ваши предложения?
Как это делать в С++ я не знаю, но нормальным решением была бы перегрузка по признаку constexpr:
В том то и проблема, что constexpr не входит в тип функции и по нему не возможна перегрузка. Это не тоже самое что спецификация const в конце метода класса.
на сppreference всё логично описано.
Не знаю про cppreference, знаю про то, какие эффекты это имеет.
Давайте начнём с простой функции, которая, согласно текущему стандарту, никогда не может быть вызвана в constant evaluated context:
Соответственно,
Detector<std::is_constant_evaluated()>::type test;
будет валидным выражением, когдаis_constant_evaluated()
возвращаетtrue
, а иначе — ошибкой компиляции. Как думаете, скомпилируется?Скомпилируется! Почему? Потому, что
is_constant_evaluated()
проверяет не то, вызывается ли содержащая её функция в константном контексте, а то, вызывается ли самаis_constant_evaluated()
в константном контексте. В принципе, на этом можно и закончить, но давайте продолжим.даёт нам
Ну, то есть, функция резко стала
false
. Потому, что контекст больше не константный.Продолжаем:
смотрим дизасм:
Ну, то есть,
true
. Ок, а так?Дизасм:
Теперь оно
false
(что мне на высказанную мной ранее модель натянуть не удалось, к слову). Что, кстати, довольно иронично: компилятор в компилтайме видит, что выражениеfalse
, но оно не является компил-тайм выражением. muh logicОк, а если создать временную переменную? Это — рефакторинг, который не должен иметь никакой семантической разницы для булов, верно?
Ага, десять раз, теперь это true:
А, чисто ради лулзов, если вот так?
Что будет? Единичка в
global
? Ошибка компиляции?Ни то, ни другое! Этот код скомпилируется успешно (то есть, на первой строчке функции там
true
), но вglobal
будет 0 (то есть, на второй строчкеfalse
).Чё-т как-то ну так себе логика.
Вполне закономерно. Вы везде пытались поменять ответ в зависимости от const_evaluated. А он предназначен для противоположной цели. И там вполне ясно сказано что тестируется выражение содержащие is_const_evaluated() а не функция в которой содержится эта инструкция. Пример с тренарником интереснее, но про него есть в документации.
Я пытаюсь посмотреть, как он работает, и построить интуицию. Интуиция не очень строится.
Чтобы не менять ответ в зависимости от него? Ну тогда его просто не надо вызывать.
Да, ответ менять не надо - надо менять реализацию. Вам хочется поднять его на уровень типов, кажется что такая возможность только добавит непонятных моментов в коде. Тогда ваш рефакторинг а ля вынесли константу в новую переменную не просто поменяет true на false, а может поменять int на float.
Чтобы исправить этот костыль в С++23 добавили if consteval { } без condition
Пробовать вычислить любую функцию в constexpr может быть проблематично, если ее вычисление занимает много времени, или вообще не известно завершима ли она. Что компиляторы должны делать в таком случае? И что они делают сейчас интересно.
Есть предел по количеству выполнимых команд, после которого компилятор откажется досчитывать метод. Заранее сказать, завершится ли функция, они никогда не смогут из-за проблемы остановки.
Ну отлично… Программа не скомпилировалась, потому что компилятор запускался на слабом компьютере ))
Но если такой предел пробовать для всех функций, то это займет больше времени, чем только для constexpr. Хотя пропагирование констант работает и для обычных функций часто, без пометки constexpr.
Ну, с оптимизациями компилятор и так всё пытается вычислить и упростить, а без оптимизаций надо вычислять только то, что используется в соответствующем контексте. Условно, видим
— вычисляем. Не видим — не вычисляем.
Т.е. он не делает разницы для constexpr и нет функций на этапе оптимизаций?
Да, не делает.
С октября 2021 года есть пропозал чтобы то, что вы описали, больше не будет ill-formed/NDR
http://open-std.org/JTC1/SC22/WG21/docs/papers/2022/p2448r1.html#pnum_24
Уж этого я правда не почитал
Можно было бы обойтись int func(args), там во всех функциях такой стиль был
Ой да, в std этого нет
Это так и есть
Боль от С++ у всех своя, кого-то одно тревожит, кого-то другое. Однако пока будут задачи, которые ничем, кроме плюсов не решить, мы будем с этим жить. В куче мест на предложение переписать всё к чертям на шарпе или JS люди улыбаются и крутят пальцем у виска. Поэтому пишите себе точку с запятой и смиритесь с этим.
К счастью, кроме шарпа и жс есть и другие языки. Да и задачи, не решаемые ни на чём, кроме плюсов, в основном связаны с наличием уже имеющейся плюсовой базы, а это так себе аргумент.
Для меня точка с запятой это как минимум якорь, за который цепляются глаза при чтении кода. Сразу видно, что вот, мол, конец выражения.
Автору советую расслабиться и получать удовольствие.
Ок. Особенно радует невозбранное использование любого числа точек с;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Набор боли от C++ у автора достаточно мало коррелирует с моим — настолько, что я не понимаю, что именно он пишет и почему. У меня тут следующее:
— Само завершение операторов (statements) знаком ';' нравится больше, чем варианты его автогенерации в конце строки, если нет причин считать, что будет продолжение (как в Go, когда последним стоит операция (operator) типа "+", или как в Python, когда явная '\' или незакрытые скобки).
А вот когда ставится ';' после '}' — тут, да, бардак. Пояснение в соседних комментариях, которое по сути сводится к отличию
от
на самом деле это последствие концептуальной слабости, что сэкономили на выделении типа:
так в большинстве языков, где синтаксис тщательнее продумали.
Второй аспект тут же — необязательность блоковых скобок в телах после 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, ближе к реальности (повторюсь — в каком именно практическом домене работает автор? Автор, признавайся!)
Автор взялся пилить игрушку на плюсах вот и задолбался от длинных имён.
= и == это даа, я вот однажды подумал что std::find при отсутствии результата вернёт 0 и сделал соответствующую проверку. После того как я уже позабыл об этом, стали появляться непонятные баги с искажением данных, которые долгое время не выстреливали и повявились только при релизной сборке
std::find
не может вернуть 0, потому что он в общем случае возвращает итератор, а итератор далеко не всегда выглядит как указатель.Взяли бы Unity
Как это вообще скомпилировалось?
Это не назначение inline, давным давно inline ТОЛЬКО про линковку
Вообще то в языке С++ компайл тайм вещи передаются в < >, а рантайм вещи передаются в ( ), про sizeof не нужно заикаться, т.к. это C
Значит вы не умеете ими пользоваться, они крайне удобны
Нет никакого другого способа разрешить компилятору хоть что то оптимизировать. Нет более хорошего пути чем введение УБ
Кому нахрен нужны именованные конструкторы? Функций не хватает?
Очень простые принципы нахождения подходящей функции - наиболее подходящая. Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.
> Это не назначение inline, давным давно inline ТОЛЬКО про линковку
(цитируется по 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 взорвётся. Ещё одна радость.)
(Можно закостылить через фиктивный параметр конструктора, но почему не напрямую через имя?)
> Очень простые принципы нахождения подходящей функции — наиболее подходящая.
Да-да-да. С подстановкой неожиданных типов через неявные конверсии.
> Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.
Зависимость от чужого кода, где уже сотворено чёрт-те что и менять не получится — совершенно типовая ситуация. Странно, что вы с этим, судя по сообщениям, не сталкивались.
Только для тех, кто не пробовал другие средства параметрического полиморфизма или метапрограммирования.
Лол, нет. Языки отлично можно оптимизировать и без UB.
Какой простор для оптимизации, кстати, открывает упомянутое мной выше UB с констекспром?
Мне тут объяснили в прошлом обсуждении про constexpr, что эта ерунда с constexpr вызвана не возможностями оптимизации, а необходимостью иногда вставлять в код отладочный вывод.
Хотя я бы на месте разработчиков стандарта добавил примитивы для constexpr-отладочного вывода вместо того чтобы разрешать constexpr иногда переставать быть constexpr.
Я даже с этой подсказкой не понял, зачем добавлять это как UB.
А как тебе такое, Илон Маск?
Да, я потом подумал, что надо бы привести это как пример тоже. Но сами подобные безымянные типы — фича не сильно важная.
И при желании и её можно было бы реализовать без ';' (я как раз этого не хочу, но принципиальная возможность налицо).
Когда в одной статье написано, что не нравится "Точка с запятой;" и "Медленная сборка", то у меня возникает логический диссонанс. Обыватели тоже цвет записывают скрытых вещей в достоинства/недостатки, но чтобы этот вопрос обсуждать...
Да, это смешно и больше связано с моими фантазиями о каком-нибудь быстром питоне
Вот тут бы мне очень хотелось чтобы поскорее добавили работоспособные модули, пришлось бы писать меньше кода, а из-за их модульности можно было бы компилировать в многопотоке
Компилировать в несколько потоков можно уже сейчас
Модули в C++ существуют ещё задолго до появления первого компилятора для него, и называется это раздельная компиляция.
И неуча неспособного выучить даже простойшую точку с запятой никто не спрашивал. Общепринятым стандартом для всех зрелых языков программирования, проверенных десятками лет, оно стало вовсе не закрасивые глаза.
Ну я имел в виду работу лексера, потому что построить однозначную таблицу переходов проще (с единственным разделителем, в нашем случае ";"), а уже по таблице разбивать на лексемы намного быстрее и можно сократить количество проходов. Даже в многопроходном языке (C++, к примеру) явный и однозначный разделитель сильно сокращает количество символов для идентификации лексемы при разборе, что позволяет выполнять параллельно лексический и синтаксический разборы без всяких ухищрений.
Тем более использовать перевод каретки как разделитель выглядит странно и лишает возможности форматировать код так, как хочется (даже несмотря на то, что любая ДОС в терминале выполняет команды по кнопке "ввод").
Когда Страуструпа неустраивал C он написал C++. Предлагаю автору не скромничать и написать свой C+++ с модулями и системами сборки.
Зачем чё-то писать, когда есть язык Brat:
p "hello world"
Функция print в одну букву P, вы только вдумайтесь
Перефразируя всемизвестную цитату:
"С++ сожет показаться ужасным языком программирования, если вы не пытаетесь написать что-то серьёзное на чем-то ещё".
Если тебе не нравится язык программирования, то просто не пиши на нем. Пиши на том, что нравится, публикуй кейсы, а не кликбейтные заголовки!
> «С++ сожет показаться ужасным языком программирования, если вы не пытаетесь написать что-то серьёзное на чем-то ещё».
В результате основная часть «энтерпрайза» ушла много лет назад на Java, C#, математики — только подключают C++ код (а местами даже Fortran) через Python или R, почти все девопс-тулзы написаны на Go, и так далее.
Да, бо́льшая часть причин этого — неумение/нежелание морочиться с ручным управлением памятью (одно из самых неудобных и чреватых скрытыми ошибками), но часть аргументов автора статьи даёт вклад и в эти миграции.
Энтерпрайз ушел в Java и C# по большей части из-за того, что там стандартная библиотека включает в себя больше нужных вещей. До сих пор в плюсах нету стандартных классов для работы с networking, включая высокоуровневые протоколы, gui и так далее. А в те времена, когда энтерпрайз уходил на Java, в std не было даже thread'ов.
> Энтерпрайз ушел в Java и C# по большей части из-за того, что там стандартная библиотека включает в себя больше нужных вещей. До сих пор в плюсах нету стандартных классов для работы с networking, включая высокоуровневые протоколы, gui и так далее.
Ну и сейчас многих вещей нет в базовой поставке Java, но есть популярные библиотеки, которые использует каждый второй (не будем вспоминать log4j, но вот хотя бы Guava библиотеки). Аналогично в Python есть всякие http.client, но все ленятся и используют requests. Тут наверняка быстро возникло бы что-то аналогичное. И для C++ есть много библиотек и фреймворков, и что, кому-то сильно плохо, что Qt не в стандарте?
В общем, сомнительный тезис.
> А в те времена, когда энтерпрайз уходил на Java, в std не было даже thread'ов.
Ну да, не было. Но переносимые библиотеки — врапперы вокруг штатных средств ОС — уже были, а уж под конкретный API мог писать каждый.
По сути, только с распространением github + cmake, использование внешних библиотек стало возможным почти так же легко, как и в python можно зацепить pip install'ом. И что там в Java модно? mawen? А раньше это надо было хорошенечко потрахаться, чтобы собрать либу под все нужные платформы, в рилизе, в дебаге и так далее. Помню openssl собирался перловым скриптом, и под винду надо было поставить перл для этого. И прочие, и прочие "ништяки". А потом выходит новая версия либы и опять всё заново.
В общем по сравнению с тем, чтобы просто взять Java, это было серьезно сложнее.
Серьезно?
ошибка на ошибке
С python перепутали видимо. Должно быть имели в виду:
(type)val
Пара мыслей про боль автора с header-only библиотеками.
Если код header-only библиотеки не меняется часто, то ускорить компиляцию поможет использование precompiled headers. В CMake, например, есть поддержка на уровне таргетов.
Вообще, если в своем проекте создавать header-only библиотеки, затем постоянно что-то в них изменять и жаловаться на долгую компиляцию - сам себе злой буратино. Библиотеки делают для распространения кода, чтобы ими пользовались другие авторы, проекты. В таком сценарии, обычно, обновление библиотеки происходит между релизами. Соответственно, проблему постоянных долгих сборок можно избежать, правильно настроив сборочный процесс.
PCH кеширует только стадию разбора деклараций. Если h-файл состоит только из деклараций, то ускорение существенное. Если же в h-файле 10 мегабайт кода, который нужно сгенерить и положить в компилируемый сейчас бинарник, на каждой компиляции этот код будет генерироваться заново.
В hpp файлах часто темплейты, и их надо разбирать при инстанциировании с новыми темплейт аргументами, и следовательно компилировать заново.
Я не углублялся в детали конкретных компиляторов, если честно. Я не подразумевал, что PCH заменит компиляцию. Каждый translation unit, в любом случае, будет перекомпилироваться, если файлы, от которых он зависит, будут изменены в процессе. Как я понял, PCH означает, что синтаксический разбор не надо будет заново производить, если не было изменений библиотеки, как минимум.
А вам спасибо, что ткнули меня носом в незнание фактуры. Надо будет посмотреть, как только потребуется, конечно.
Наверно, все от скуки и безделья, надо заняться делом.
Нигде нет идеала, мы ко всему подстраиваемся или меняем религию.
Я думаю об алгоритме и т.д., а не о точке с запятой :D
Но точка с запятой не влияет на алгоритм, только на C++
Много места в ОЗУ занимает эта точка с запятой, фигурная скобка? Палец устал?
Это как мем, где 500 разных арифмитических выражений без скобок в одном выражении, можно и так писать, но я для ясности буду делать осмысленные скобки.
Я после каждой закрывающей скобки блока (даже если там всего две строчки) делаю комментарий к чему она относится (на автомате) // if, // else, // while и т.д.
Код читабельнее. И ни как не напрегает.
А вот длинное reinterpret_cast. Согласен.