Pull to refresh

Comments 138

Существует ли случай, в котором отсутствие; могло бы вызвать критическую ошибку всей программы?

Да.


auto p = a; -b;
auto p = a -b;


auto p = a; (b=5);
auto p = a(b=5);


Я не сишник, уверен сишники сейчас еще десяток примеров приведут.

Сейчас автор скажет, что разделять надо переводами строк и тогда всё будет ОК =)

Не, автор скажет мол незачем в конце строки ставить. Для разделения a, b или a; b было бы норм

while ('\0' != (*dst++ = *src++))
    /* just copying */;
Ну для пустого оператора вполне можно было бы nop завести :)

Я тут всего лишь "крокодил мимо", но вот у нас в фортране операторы можно разделять точкой с запятой, но при этом ставить ";" в конце каждой строки совершенно не обязательно:

select case (int_Year)
  case(:1917);  	name=get_emperor(int_Year); 	exit main_cycle
  case(1918:1991);  return 1914
end select

Конечно, при этом приходится терпеть отдельный символ для переноса (продолжения) оператора на несколько строк, т.к. по умолчанию конец строки= конец оператора. Но писать его обычно приходится гораздо реже.

Кстати, строка "return 1914"

в фортране означает совсем не то, что некоторые могли подумать, а так называемый "альтернативный возврат". А именно, управление будет передано не на следующий после call оператор, а на 1914-ю по счету метку в списке фактических параметров процедуры. Как сказал один классик, "эта штука сильнее, чем GOTO!" (c). И - да, такой оператора call (с соответствующим списком меток) без переноса на следующую строку не напишешь ;-)

P.S. Даже интересно, почему при такой любви к лаконичности в Си-семействе еще никто не попытался "залюбить" символ ";" в конце строчки ;-)

А если хочется наоборот — одну команду на несколько строк?


По моему все-таки в с++ и прочих джавах сделано лучше. Когда непечатаемые символы вроде переноса строки или табуляции (питон) влияют на логику программы с этим становится опаснее работать в обычных текстовых редакторах.

Табуляцию не принято использовать в питоне, только пробелы. С ними не возникает проблем из-за разной длины табуляции.

А если хочется наоборот — одну команду на несколько строк?

Не знаю, как в других языках, но вот в фортране символ переноса строки (точнее, продолжения оператора на следующую строку) вполне себе печатаемый: &. То есть, его

надо написать явно:
Res = EXP (Y) + &        ! The initial statement line
      EXP(-Y)            ! A continuation line
      
MyText  = "This is &   
      &a text"

Кстати, я сам не знал, что у нас можно разбивать литерал на несколько строк. Всегда писал что-то наподобие вот такого:

MyMessage  = "Это первая половина сообщения "// &
"а это вторая"

Может, это вопрос привычки, но в таком формате мне легче читать и с пробелами как-то проще...

Понятно, что у фортрана в силу древности происхождения набор символов изначально весьма ограничен. Например, логическое И пишется, как Condition1.AND.Condition2, а побитовое - это вообще функция (пишется, как IAND(Int1,Int2); впрочем, если она нужна часто, можно перегрузить оператор). Поэтому найти свободный символ для знака продолжения строки, когда такая идея возникла, было не сложно ;-).

Но даже если в языке все символы уже заняты, кто мешает использовать для знака продолжения оператора двойной символ? Ведь даже в очень-очень просторном литературном фортране строки с продолжением встречаются крайне редко. А в языках, стремящихся к лаконичности, наверное, еще реже? Поэтому чисто теоретически (глядя со стороны) возникает впечатление, что изредка написать дополнительный символ продолжения оператора было бы быстрее и проще, чем ставить точку с запятой в конце каждой строки. Но тем не менее это не принято и такой вариант даже не обсуждается. Если это не секретная информация, было бы интересно узнать - почему?

И прав будет. Всрато писать - ловить всратые ошибки. Play stupid games, win stupid prises.

Написать повсратее это вообще спецолимпиада с/с++. Хотя там можно писатт очень читаемо с нулевой ценой.

Ps сам не против точки и скобочек, с ними как то более явно что ли...

Существует ли случай, в котором отсутствие; могло бы вызвать критическую ошибку всей программы?
Классический пример из K&R
void strcpy(char* dst, const char* src)
{
        while (*dst++ = *src++);
        *dst++ = 0;
}

Похоже что пока в C++ будет возможность писать такой код, он всегда будет останется с точкой с запятой

UFO just landed and posted this here
Но автор хочет не так, он хочет
исбавление от него не вызвало бы никаких проблем — для совместимости старые программы бы просто учитывали точку с запятой, а новые бы понимали что отсутствие символа можно проигноровать
И вот тут, с его предложением, поломаются старые программы. То есть, предложение не рабочее.
UFO just landed and posted this here
Он считает (считал), что если точку с запятой сделать опциональной, то ничего не сломается, т.к. всегда можно понять из контекста, лишняя она или не лишняя. Мой пример показывает случай, когда поведение меняется, и компилятор автоматом не сможет вычислить, пропущена точка с запятой умышленно, или это «новый синтаксис».

автор хотел как в js скорее всего

Я зумаю автор имел ввиду точку с запятой вместо переноса строки.

но вот время компиляции такой программы может затянуться секунд на 30

Эх, в 87 году компиляция программы в 1200 строк на Паскале занимала 30 минут

Игры кое-какой фирмы на движке Alien Engine с доработками как раз компилились у меня 30 мин на компе с Core 2 Quad, но это хотя бы один раз надо так потерпеть при полной пересборке. В офисе той фирмы даже поставили сервер компиляции

 Вообще, в одном китайском эмуляторе сеги я видел примерно это:
constexpr static inline auto func(args) -> int noexcept const

Не читайте до обеда советских газет исходники китайских эмуляторов.

[dcl.consexpr] - constexpr-метод неявно является inline.

static inline бессмысленный и работает как просто static. И сам static не нужен, когда есть constexpr. (Если этот метод глобальный)

Про "второе значение" слова inline, про встраивание кода - примерно с 2013 года Clang перестал ставить таким методам флаг inlinehint в своем промежуточном представлении, и сейчас это значение утратилось. У других компиляторов скорее всего так же.

 Вообще, в одном китайском эмуляторе сеги я видел примерно это:

constexpr static inline auto func(args) -> int noexcept const

Какой смысл в модификаторах static inline?

Если подразумевается, что функция вычисляется на стадии компиляции и в сегменте кода (или ещё где либо) её просто нет.

Судя по тому, что у функции модификатор const это мембер класса и смысл в static как раз может быть, а вот constexpr и так автоматом делает такую функцию inline. Т.е. как раз inline тут лишний.

да, constexpr static inline сами себя аннигилируют, но там это видимо сделано на всякий случай

А В MINGW ЭТО НЕ МЕМ!.

В том что в Mingw вам понадобится отдельно докачать Parallel STL

GCC и Mingw-w64 уже умеют в модули, но с 11-й версии

Где здесь проблема C++?

Бывает и что в папке с русскими символами возникают проблемы с путями в самой программа, особенно в Windows XP.

А MS-DOS 6.22 не поддерживает имена файлов длиннее, чем 8.3. Нам как, уже начинать считать проблемы OS, вышедшей всего-то за 7 лет до Windows XP, как критичные?

но касты в коде встречаются часто и их многократное использование в одной строке очень сильно удлинняет код

В нормальном коде касты встречаются достаточно редко

вам пришлось писать лямбды, которые наверное бы воспринимались как std::function и медленно бы вызывались.

Это "наверное", говорит очень многое

Где здесь проблема C++?

Проблема компилятора

Нам как, уже начинать считать проблемы OS, вышедшей всего-то за 7 лет до Windows XP, как критичные?

На Доске может никто и не сидит но вот ноуты с икспишкой попадаются часто

Не знаю как в C, а в Rust - ; - это очень важная часть синтаксиса, она делает возвращаемый тип unit (()).

Т.е. fn foo() -> i32 {4} и fn foo() -> i32 {4;} - это две большие разницы, второе даже не скомпилируется. Благодаря тому, что ; разделяет выражения, можно писать блок ({}) в любом месте.

Например, так:
let x = {println("hello here"); 4};

Можно было бы просто сделать scast, dcast и rcast

Офигенно длинные выражения для кастов - не ошибка и не случайность. Это сделано ПРЕДНАМЕРЕННО. Чтобы вызывать чувство дискомфорта. Каст - исключительная конструкция, которой в норме не должно быть. Если увас много кастов - что-то в коде у вас не то. Говнокодом попахивает.

а после структур и классов нужна?

Таки исторически сложилось, что структуры и классы определяют тип. И после определения типа вы можете сразу определить переменную данного типа. Точка с запятой говорит, что вы эту возможность не использовали. Все остальное - обратная совместимость.

Лично меня ни скобки, ни точки с запятой не напрягают.

Странно, что автор не пожаловался на необязательность выравнивания кода, если вы понимаете, о чем я =)

Читаю и думаю: прикольно, понятное объяснение.
Потом читаю ник.
Теперь не могу понять маразм это или правда.

Каст - исключительная конструкция

Ну а если надо кастовать данные в байты при работе с файлами или сделать динамик каст с базового класса, разве это плохо?

Если вам приходится делаеть динамик каст, то вы априори что-то не то надизайнили) Тем более могут быть определенные проблемы с RTTI, в зависимости от компилятора.

Да, в IO функциях кастовать не воспрещается, и временами вовсе необходимо: передать массив как массив байт, а так же сделать memcpy(не reinterpret_cast!) при обратном преобразовании массива байт в объекты.

C++20 для обоих случаев даже специальные библиотечные функции получил: std::as_bytes, std::bit_cast.

Частое же употребление кастов в прикладном коде - признак плохого дизайна. Лично у меня временами такое тоже случается и тоже подгорает, но тут надо не забывать, что плохой код у меня - мне его и переписывать, разработчики языка в этом не виноваты.

Ну а если надо кастовать данные в байты при работе с файлами

Это и есть та самая редкая исключительная ситуация.

или сделать динамик каст с базового класса, разве это плохо?

Это - очень плохо. Нарушение буквы L в принципах SOLID. Базовый класс не должен знать ничего про потомков. Как раз здесь и должна сыграть свою роль ужасность конструкции каста.

Базовый класс не должен знать ничего про потомков.

А вы слышали про CRTP?

Формально, там принцип подстановки не нарушается, потому что
template <typename T> class Base { }
не является классом до указания типа T.

В конструкции
class Derived : Base<Derived> { } 
Класс Base«Derived» знает про тип Derived, потому что это его параметр шаблона, а таким способом знать разрешается.

Весь раздел про синтаксис - демонстрация его полного незнания и непонимания. Хоть бы почитали, откуда взялась эта точка с запятой, особенно после классов.

В некоторых других моментах есть зерно истины, но накидали в одну кучу так, что пользы - ноль. Даже крик души полезно структурировать.

Можно было бы понаделать статей на разные крики души, но они сами по себе и так бесполезны и ничего бы не решили. Разрабы компиляторов и сборочных систем наверное не читают хабр. Остаётся только направленно указывать им в пул реквестах свои хотелки и рассказывать о проблемах

В волшебном мире линукса вам int main(argc, argv) может принять аргументы в UTF-8 и даже не поперхнуться, а вот на винде вам понадобится использовать системнозависимый функционал или wmain, которого нету в GCC и Clang

Участвовал в проекте, где по неосторожности ради разработчиков, пишуших под виндой на старте проекта было принято решение использовать wide character'ы для мультиплатформенности и соответственно std::wstring. В итоге:
1) Теперь всё это дерьмо в Linux (а это таргет платформа) конвертируется из utf-8 в UCS-4, что сжирает порой много времени на работу iconv
2) В винде всё равно используется кастрированный юникод на два байта шириной.
3) Везде приходится писать L"".

И так далее. Иногда это вылезает в самых неожиданных местах.

На мой взгляд идти по пути системы, создатели которой всегда выбирают (или выбирали) альтернативно-одаренный путь чисто на зло всему остальному миру стандартов, это путь испытать на себе весь набор грабель в лоб.

Если вы пишите юзерский софт - закройте эти проблемы библиотекой типа Qt. Если пишете серверный/системный софт - просто забейте на винду.

… на старте проекта было принято решение использовать wide character'ы для мультиплатформенности и соответственно std::wstring. В итоге:
А какая связь между UNICODE конфигурацией WinAPI и выбранным вами std::wstring. Что мешало выбрать std::string или std::u8string и использовать UTF-8?

 Что мешало выбрать .... std::u8string ....?

Как минимум 2010 год.

Что мешало выбрать std::string .... и использовать UTF-8?

И много ли полезного можно сделать с 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 в блокноте, на сколько я помню.

Лучше тогда сразу за место wstring брать u8string и u8"Строки"

Подучите сначала современный С++, а потом критикуйте.

Полностью согласен. Как говорится, сказал как отрезал, ни добавить, ни убавить.

Почему после неймспейсов точка с запятой не нужна, а после структур и классов нужна?
Потому-что возможна декларация одновременно с объявлением.
Можно было бы просто сделать 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.
Большинство проблем натянуты и мучают лично меня одного, просто захотел высказаться.
Прям так же и хочется ответить. Полное ощущение что статья стоит из потока мыслей, с надерганными откуда-то случайными примерами, без малейшей попытки разобраться что и как и без понимания какие реальные проблемы есть в С++.
UFO just landed and posted this here
Почему все функции не могли бы быть implicitly constexpr, непонятно.
… в Комитете сидят либо напрочь оторванные от практики люди, либо психопаты, ненавидящие программистов.
Как не парадоксально, но может оказаться совсем наоборот — эту фичу пилили какие-нибудь практики, которые решали свою, сугубо локально задачу, которые были так этим заняты, что у них не было уже сил поднять голову и осмотреться по сторонам. Об этом косвенно свидетельствует наличие проблемы на которую я наткнулся в своём пет-проекте. Вот как можно было в стандарте зафиксировать, что constexpr метод может отработать во время компиляции, а может и не отработать, но при этом не подумать, что бывает что реализации существенно отличаются и не дать инструментов которые дали бы возможность определить в каком режиме сейчас находится код ?! И только в С++20 вдруг очнулись и добавили std::is_constant_evaluated().
UFO just landed and posted this here
Я бы сам хотел что бы компилятор всё оптимизировал за меня и сам обо всём догадывался, но ресурсы и память компилятора не бесконечны, к сожалению.
Если реализации сильно отличаются, то, возможно, им просто не место в одной функции.
Речь не идет о том что бы всё помещать в одну функцию конечно, но в любом случае есть точка принятия решения и её не реализовать без std::is_constant_evaluated().
ИМХО std::is_constant_evaluated() — костыль с жутко неинтуитивным поведением. Ситуации, описанные в пропозале, который я читал года три назад, выглядят какими-то уж больно натянутыми, и мне при всей моей любви к компилтайм-упарыванию ни разу не хотелось этой функцией воспользоваться.
Предположим у вас есть шаблонная функция, которая вас устраивает во всех ситуация, в том числе в constexpr режиме. Теперь, предположим, что в случае если шаблонный параметр является целым, кратен 4-м байтам и код выполняется в рантайме, вы можете сильно оптимизировать алгоритм на GPU или ещё как-нибудь. Какое ваше предложение, что делать?
UFO just landed and posted this here
Просто написать две разные функции.
Функции физически разные, но одна должна являться перегрузкой другой, т.к. они вызываются в одном и том же контексте, в зависимости от шаблонного параметра и режима выполнения (constexpr). Можете привести пример С++ кода который так может без std::is_constant_evaluated?
UFO just landed and posted this here
Отлично, согласен! Заодно может быть вы поможете мне решить вполне практическую задачу. Я вам буду премного благодарен. Поехали! Есть класс: long_uint.h: line 43
template<typename native_t, uint_t size>
class long_uint_t<native_t, size>;
Класс реализует арифметику над длинными целыми, в том числе во время компиляции. Нам сейчас не важны все операции возьмем один оператор — меньше (<): long_uint.h: line 279
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;
}
Реализация у него в виде шаблона и очень простая: вычитаем из первого аргумента второй и если произошел перенос в старшем бите, то первый аргумент меньше. Само вычитание c переносом реализовано через функцию subb (Subtract with borrow): long_math.h: line 437. Функция subb() полностью платформонезависима и без проблем работает в constexpr выражениях:
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
{
    type_t tmp = value1;
    value1 -= value2;
    bool borrow_new = value1 > tmp;
    tmp = value1;
    value1 -= borrow;
    borrow_new = borrow_new || (value1 > tmp);

    return borrow_new;
}
Дальше, на некоторых платформах, например Windows, мы можем выполнять данную операцию намного эффективнее для 32-х битных беззнаковых целых, делаем перегрузку: long_math_msvc.h: line 372
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() я не знаю. Какие ваши предложения?

Как это делать в С++ я не знаю, но нормальным решением была бы перегрузка по признаку 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);
}
В том то и проблема, что constexpr не входит в тип функции и по нему не возможна перегрузка. Это не тоже самое что спецификация const в конце метода класса.
Поскольку у вас не полный пример, можно снять требование, чтобы эти функции назывались одинаково. Например, пусть runtime функция называется subb_fast, а constexpr — subb.

Очевидно почему так плохо - придется два варианта писать для всех constexpr функций которые ее используют.

Точно, я не заметил что subb входит в operator <, а ему даже другое имя дать нельзя.
UFO just landed and posted this here

Ну, мы же обсуждаем не текущий С++, а гипотетический, который мог бы быть если бы проблему не подпёрли костылём-std::is_constant_evaluated.

Сигнатура subb() c constexpr включает в себя случай subb() c inline. Компилятор может вычислить результат constexpr функции во время компиляции, а может просто оставить вызов как обычной функции, которая будет вычислена в рантайм. Всё это зависит от контекста вызова, так что я не представляю какую перегрузку он должен был бы выбрать.

Ну вот пусть и выбирал бы в зависимости от того какое решение он принял — считать во время компиляции или оставить обычный вызов функции.

Чем отличается от if consteval внутри тела функции?

Не могли бы вы развернуть свой вопрос, ничего не понятно?
constevalЭто то же почти тоже самое что и constexpr, только функция должна работать только в режиме компиляции. Если функция в заданном контексте не может быть вызвана во время компиляции, то это приведет к ошибке компиляции.

Я про `if consteval` из C++23. Т.е. вот автор предлагает делать две функции и перегрузку по constexpr модифиактору. Я спросил, чем эта перегрузка отличается от if consteval внутри одной функции. Например одно отличие, что можно было бы добавлять перегрузки отдельно. В любом случае constexpr уже заняли больше 10 лет назад для другой цели.

Тем, что if consteval вроде как добавили много позже чем std::is_constant_evaluated

UFO just landed and posted this here
Хорошо. Давай теперь посмотрим к чему приведет ваше решение, а скорее не решение, а заметание проблемы под ковёр:
  1. В данном случае это библиотека и мы тем более не должны выносить сложною на ваш взгляд логику наружу и перекладывать возможность ошибки и неправильного использования на пользователя библиотеки. Полезность библиотеки от этого будет только страдать.
  2. Теперь эти два класса нельзя будет использовать в constexpr коде, так как мы не контролируем внешний код и не знаем в каком контексте наш класс будет использоваться.
  3. Пользователь библиотеки будем вынуждены менять весь свой код во время разработки, если в коде будет меняться контекст использования класса, то constexpr, то runtime.
  4. Во внешнем коде будет расти комбинаторные сочетания вспомогательных функций, так как мы будем вынуждены преобразовывать один класс в другой, передавать эти два класса как параметры в разных сочетаниях. Это можно легко представить на таком примере: представьте что у вас std::vector не может быть константным, а вместо этого будет отдельный класс std::const_vector — как же удобно будет ними двумя работать и как быстро вы получите комбинаторный взрыв ?!

В общем я пожалуй останусь при своём мнении, что std::is_constant_evaluated() это меньшее из многих зол и при уместном использовании позволяет быть коду надежнее, проще и гибче, а следовательно полезная и нужная функция.

Согласен. Писать два вида чисел, как предлает человек выше - это писать в два раза больше кода на каждый такой if и комбинаторный взрыв в результате.

UFO just landed and posted this here

Сомневаюсь что механизм перегрузок в С++ проще (с точки зрения потенциальных ошибок) одного ифа.

UFO just landed and posted this here

Написать линт для clang-tidy - что is_constant_evaluated только внутри if (...).

Что значит "вдруг" начнет возвращать не то?
предлагаете рефакторинг в результате которого стало что-то типа такого:

bool ce = std::is_constant_evaluated();
if (ce) { ... } else { ... }

т.е. вынесли ce в отдельную переменную? В этом плане if consteval более эрогономичный. А если кто-то реализовал на основе него std::is_constant_evaluated то это на самом деле плохая идея, потому что ответ в кт и рт должен совпадать.

PS Новый редактор хабра вообще отбивает желание сюда писать :(

Временное решение )

#define if_consteval \
if (std::is_constant_evaluated())

Хах, диверсия. Хотя и баян

Мне еще нравится такое:

#define loop while(true)

Мне кажется такой цикл в некоторых (но не во всех) местах лаконичней и понятней, можно if (...) break поставить не только в начале, как while, и не только в конце, как в do ... while. Но, понятно, что на практике не в своих проектах, такое лучше не использовать, а обойтись while(true)

Прикольно с помощью макросов создавать новые простенькие ключевые слова, которые меняют сам синтаксис. ) Главное, чтобы не до неочевидности и было лаконично

на сppreference всё логично описано.

UFO just landed and posted this here

Вполне закономерно. Вы везде пытались поменять ответ в зависимости от const_evaluated. А он предназначен для противоположной цели. И там вполне ясно сказано что тестируется выражение содержащие is_const_evaluated() а не функция в которой содержится эта инструкция. Пример с тренарником интереснее, но про него есть в документации.

UFO just landed and posted this here

Да, ответ менять не надо - надо менять реализацию. Вам хочется поднять его на уровень типов, кажется что такая возможность только добавит непонятных моментов в коде. Тогда ваш рефакторинг а ля вынесли константу в новую переменную не просто поменяет true на false, а может поменять int на float.

Чтобы исправить этот костыль в С++23 добавили if consteval { } без condition

UFO just landed and posted this here

Пробовать вычислить любую функцию в constexpr может быть проблематично, если ее вычисление занимает много времени, или вообще не известно завершима ли она. Что компиляторы должны делать в таком случае? И что они делают сейчас интересно.

или вообще не известно завершима ли она. Что компиляторы должны делать в таком случае?

Есть предел по количеству выполнимых команд, после которого компилятор откажется досчитывать метод. Заранее сказать, завершится ли функция, они никогда не смогут из-за проблемы остановки.

Ну отлично… Программа не скомпилировалась, потому что компилятор запускался на слабом компьютере ))

Но если такой предел пробовать для всех функций, то это займет больше времени, чем только для constexpr. Хотя пропагирование констант работает и для обычных функций часто, без пометки constexpr.

UFO just landed and posted this here

Т.е. он не делает разницы для constexpr и нет функций на этапе оптимизаций?

UFO just landed and posted this here

почему специально так было сделано

Уж этого я правда не почитал

А что здесь лишнего по мнению автора?

Можно было бы обойтись int func(args), там во всех функциях такой стиль был

Что за контейнер такой ???

Ой да, в std этого нет

Прям так же и хочется ответить. Полное ощущение что статья стоит из потока мыслей, с надерганными откуда-то случайными примерами, без малейшей попытки разобраться что и как и без понимания какие реальные проблемы есть в С++.

Это так и есть

Боль от С++ у всех своя, кого-то одно тревожит, кого-то другое. Однако пока будут задачи, которые ничем, кроме плюсов не решить, мы будем с этим жить. В куче мест на предложение переписать всё к чертям на шарпе или JS люди улыбаются и крутят пальцем у виска. Поэтому пишите себе точку с запятой и смиритесь с этим.

UFO just landed and posted this here

Для меня точка с запятой это как минимум якорь, за который цепляются глаза при чтении кода. Сразу видно, что вот, мол, конец выражения.

Автору советую расслабиться и получать удовольствие.

Ок. Особенно радует невозбранное использование любого числа точек с;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Набор боли от 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, ближе к реальности (повторюсь — в каком именно практическом домене работает автор? Автор, признавайся!)

в каком именно практическом домене работает автор? Автор, признавайся!)

Автор взялся пилить игрушку на плюсах вот и задолбался от длинных имён.

= и == это даа, я вот однажды подумал что std::find при отсутствии результата вернёт 0 и сделал соответствующую проверку. После того как я уже позабыл об этом, стали появляться непонятные баги с искажением данных, которые долгое время не выстреливали и повявились только при релизной сборке

UFO just landed and posted this here

Как это вообще скомпилировалось?

Не видел такого. В основном это таки по назначению — просьба встроить.

Это не назначение inline, давным давно inline ТОЛЬКО про линковку

Но тут бо́льшая диверсия (как во всей системе templates) это использование <> как скобок. Здесь спокойно хватило бы и круглых скобок.

Вообще то в языке С++ компайл тайм вещи передаются в < >, а рантайм вещи передаются в ( ), про sizeof не нужно заикаться, т.к. это C

темплеты C++ сильны, но крайне неудобны

Значит вы не умеете ими пользоваться, они крайне удобны

Все undefined behavior (UdB), от переполнения знаковых 

Нет никакого другого способа разрешить компилятору хоть что то оптимизировать. Нет более хорошего пути чем введение УБ
Кому нахрен нужны именованные конструкторы? Функций не хватает?
Очень простые принципы нахождения подходящей функции - наиболее подходящая. Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.

> Это не назначение 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 взорвётся. Ещё одна радость.)
(Можно закостылить через фиктивный параметр конструктора, но почему не напрямую через имя?)

> Очень простые принципы нахождения подходящей функции — наиболее подходящая.

Да-да-да. С подстановкой неожиданных типов через неявные конверсии.

> Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.

Зависимость от чужого кода, где уже сотворено чёрт-те что и менять не получится — совершенно типовая ситуация. Странно, что вы с этим, судя по сообщениям, не сталкивались.
UFO just landed and posted this here

Мне тут объяснили в прошлом обсуждении про constexpr, что эта ерунда с constexpr вызвана не возможностями оптимизации, а необходимостью иногда вставлять в код отладочный вывод.


Хотя я бы на месте разработчиков стандарта добавил примитивы для constexpr-отладочного вывода вместо того чтобы разрешать constexpr иногда переставать быть constexpr.

UFO just landed and posted this here
struct {
  int a;
  int b;
} moo;

А как тебе такое, Илон Маск?

Да, я потом подумал, что надо бы привести это как пример тоже. Но сами подобные безымянные типы — фича не сильно важная.
И при желании и её можно было бы реализовать без ';' (я как раз этого не хочу, но принципиальная возможность налицо).

Когда в одной статье написано, что не нравится "Точка с запятой;" и "Медленная сборка", то у меня возникает логический диссонанс. Обыватели тоже цвет записывают скрытых вещей в достоинства/недостатки, но чтобы этот вопрос обсуждать...

Точка с запятой;

Да, это смешно и больше связано с моими фантазиями о каком-нибудь быстром питоне

Медленная сборка

Вот тут бы мне очень хотелось чтобы поскорее добавили работоспособные модули, пришлось бы писать меньше кода, а из-за их модульности можно было бы компилировать в многопотоке

Компилировать в несколько потоков можно уже сейчас

Модули в 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, это было серьезно сложнее.

В старой сишке обходились записью type(val) для преобразования типов

Серьезно?

В старой сишке обходились записью type(val) для преобразования типов...

С python перепутали видимо. Должно быть имели в виду:

(type)val

UFO just landed and posted this here
PCH кеширует только стадию разбора деклараций. Если h-файл состоит только из деклараций, то ускорение существенное. Если же в h-файле 10 мегабайт кода, который нужно сгенерить и положить в компилируемый сейчас бинарник, на каждой компиляции этот код будет генерироваться заново.

В hpp файлах часто темплейты, и их надо разбирать при инстанциировании с новыми темплейт аргументами, и следовательно компилировать заново.

UFO just landed and posted this here

Наверно, все от скуки и безделья, надо заняться делом.

Нигде нет идеала, мы ко всему подстраиваемся или меняем религию.

Я думаю об алгоритме и т.д., а не о точке с запятой :D

Но точка с запятой не влияет на алгоритм, только на C++

Много места в ОЗУ занимает эта точка с запятой, фигурная скобка? Палец устал?

Это как мем, где 500 разных арифмитических выражений без скобок в одном выражении, можно и так писать, но я для ясности буду делать осмысленные скобки.

Я после каждой закрывающей скобки блока (даже если там всего две строчки) делаю комментарий к чему она относится (на автомате) // if, // else, // while и т.д.

Код читабельнее. И ни как не напрегает.

А вот длинное reinterpret_cast. Согласен.

поразительный текст... у меня челюсть отвисла

буду считать что я его никогда не видел

Sign up to leave a comment.

Articles