Comments 138
Существует ли случай, в котором отсутствие; могло бы вызвать критическую ошибку всей программы?
Да.
auto p = a; -b;
auto p = a -b;
auto p = a; (b=5);
auto p = a(b=5);
Я не сишник, уверен сишники сейчас еще десяток примеров приведут.
Сейчас автор скажет, что разделять надо переводами строк и тогда всё будет ОК =)
Не, автор скажет мол незачем в конце строки ставить. Для разделения a, b или a; b было бы норм
Я тут всего лишь "крокодил мимо", но вот у нас в фортране операторы можно разделять точкой с запятой, но при этом ставить ";" в конце каждой строки совершенно не обязательно:
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++ будет возможность писать такой код, он всегда будет останется с точкой с запятой
исбавление от него не вызвало бы никаких проблем — для совместимости старые программы бы просто учитывали точку с запятой, а новые бы понимали что отсутствие символа можно проигнороватьИ вот тут, с его предложением, поломаются старые программы. То есть, предложение не рабочее.
автор хотел как в js скорее всего
Я зумаю автор имел ввиду точку с запятой вместо переноса строки.
del
но вот время компиляции такой программы может затянуться секунд на 30
Эх, в 87 году компиляция программы в 1200 строк на Паскале занимала 30 минут
Вообще, в одном китайском эмуляторе сеги я видел примерно это:
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?
Если подразумевается, что функция вычисляется на стадии компиляции и в сегменте кода (или ещё где либо) её просто нет.
да, 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, а в 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 я не помню. Как сейчас не знаю.
И если серьёзно подходить, то никакая это не проблема С++. Это проблема неудачно выбранного технического решения. Максимум проблема API на разных OS.
Блокнот давно уже поддерживает 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.
Большинство проблем натянуты и мучают лично меня одного, просто захотел высказаться.Прям так же и хочется ответить. Полное ощущение что статья стоит из потока мыслей, с надерганными откуда-то случайными примерами, без малейшей попытки разобраться что и как и без понимания какие реальные проблемы есть в С++.
Почему все функции не могли бы быть implicitly constexpr, непонятно.
… в Комитете сидят либо напрочь оторванные от практики люди, либо психопаты, ненавидящие программистов.Как не парадоксально, но может оказаться совсем наоборот — эту фичу пилили какие-нибудь практики, которые решали свою, сугубо локально задачу, которые были так этим заняты, что у них не было уже сил поднять голову и осмотреться по сторонам. Об этом косвенно свидетельствует наличие проблемы на которую я наткнулся в своём пет-проекте. Вот как можно было в стандарте зафиксировать, что constexpr метод может отработать во время компиляции, а может и не отработать, но при этом не подумать, что бывает что реализации существенно отличаются и не дать инструментов которые дали бы возможность определить в каком режиме сейчас находится код ?! И только в С++20 вдруг очнулись и добавили std::is_constant_evaluated().
Если реализации сильно отличаются, то, возможно, им просто не место в одной функции.Речь не идет о том что бы всё помещать в одну функцию конечно, но в любом случае есть точка принятия решения и её не реализовать без std::is_constant_evaluated().
ИМХО std::is_constant_evaluated() — костыль с жутко неинтуитивным поведением. Ситуации, описанные в пропозале, который я читал года три назад, выглядят какими-то уж больно натянутыми, и мне при всей моей любви к компилтайм-упарыванию ни разу не хотелось этой функцией воспользоваться.Предположим у вас есть шаблонная функция, которая вас устраивает во всех ситуация, в том числе в constexpr режиме. Теперь, предположим, что в случае если шаблонный параметр является целым, кратен 4-м байтам и код выполняется в рантайме, вы можете сильно оптимизировать алгоритм на GPU или ещё как-нибудь. Какое ваше предложение, что делать?
Просто написать две разные функции.Функции физически разные, но одна должна являться перегрузкой другой, т.к. они вызываются в одном и том же контексте, в зависимости от шаблонного параметра и режима выполнения (constexpr). Можете привести пример С++ кода который так может без std::is_constant_evaluated?
template<typename native_t, uint_t size>
class long_uint_t<native_t, size>;
Класс реализует арифметику над длинными целыми, в том числе во время компиляции. Нам сейчас не важны все операции возьмем один оператор — меньше (<): long_uint.h: line 279template<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 372inline 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);
}
Ну, мы же обсуждаем не текущий С++, а гипотетический, который мог бы быть если бы проблему не подпёрли костылём-std::is_constant_evaluated.
Чем отличается от if consteval внутри тела функции?
Я про `if consteval` из C++23. Т.е. вот автор предлагает делать две функции и перегрузку по constexpr
модифиактору. Я спросил, чем эта перегрузка отличается от if consteval
внутри одной функции. Например одно отличие, что можно было бы добавлять перегрузки отдельно. В любом случае constexpr уже заняли больше 10 лет назад для другой цели.
Тем, что if consteval вроде как добавили много позже чем std::is_constant_evaluated
- В данном случае это библиотека и мы тем более не должны выносить сложною на ваш взгляд логику наружу и перекладывать возможность ошибки и неправильного использования на пользователя библиотеки. Полезность библиотеки от этого будет только страдать.
- Теперь эти два класса нельзя будет использовать в constexpr коде, так как мы не контролируем внешний код и не знаем в каком контексте наш класс будет использоваться.
- Пользователь библиотеки будем вынуждены менять весь свой код во время разработки, если в коде будет меняться контекст использования класса, то constexpr, то runtime.
- Во внешнем коде будет расти комбинаторные сочетания вспомогательных функций, так как мы будем вынуждены преобразовывать один класс в другой, передавать эти два класса как параметры в разных сочетаниях. Это можно легко представить на таком примере: представьте что у вас std::vector не может быть константным, а вместо этого будет отдельный класс std::const_vector — как же удобно будет ними двумя работать и как быстро вы получите комбинаторный взрыв ?!
В общем я пожалуй останусь при своём мнении, что std::is_constant_evaluated() это меньшее из многих зол и при уместном использовании позволяет быть коду надежнее, проще и гибче, а следовательно полезная и нужная функция.
Что значит "вдруг" начнет возвращать не то?
предлагаете рефакторинг в результате которого стало что-то типа такого:
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 всё логично описано.
Вполне закономерно. Вы везде пытались поменять ответ в зависимости от const_evaluated. А он предназначен для противоположной цели. И там вполне ясно сказано что тестируется выражение содержащие is_const_evaluated() а не функция в которой содержится эта инструкция. Пример с тренарником интереснее, но про него есть в документации.
Чтобы исправить этот костыль в С++23 добавили if consteval { } без condition
Пробовать вычислить любую функцию в constexpr может быть проблематично, если ее вычисление занимает много времени, или вообще не известно завершима ли она. Что компиляторы должны делать в таком случае? И что они делают сейчас интересно.
или вообще не известно завершима ли она. Что компиляторы должны делать в таком случае?
Есть предел по количеству выполнимых команд, после которого компилятор откажется досчитывать метод. Заранее сказать, завершится ли функция, они никогда не смогут из-за проблемы остановки.
С октября 2021 года есть пропозал чтобы то, что вы описали, больше не будет ill-formed/NDR
http://open-std.org/JTC1/SC22/WG21/docs/papers/2022/p2448r1.html#pnum_24
почему специально так было сделано
Уж этого я правда не почитал
А что здесь лишнего по мнению автора?
Можно было бы обойтись int func(args), там во всех функциях такой стиль был
Что за контейнер такой ???
Ой да, в std этого нет
Прям так же и хочется ответить. Полное ощущение что статья стоит из потока мыслей, с надерганными откуда-то случайными примерами, без малейшей попытки разобраться что и как и без понимания какие реальные проблемы есть в С++.
Это так и есть
Боль от С++ у всех своя, кого-то одно тревожит, кого-то другое. Однако пока будут задачи, которые ничем, кроме плюсов не решить, мы будем с этим жить. В куче мест на предложение переписать всё к чертям на шарпе или JS люди улыбаются и крутят пальцем у виска. Поэтому пишите себе точку с запятой и смиритесь с этим.
Для меня точка с запятой это как минимум якорь, за который цепляются глаза при чтении кода. Сразу видно, что вот, мол, конец выражения.
Автору советую расслабиться и получать удовольствие.
— Само завершение операторов (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 и сделал соответствующую проверку. После того как я уже позабыл об этом, стали появляться непонятные баги с искажением данных, которые долгое время не выстреливали и повявились только при релизной сборке
Не видел такого. В основном это таки по назначению — просьба встроить.
Это не назначение inline, давным давно inline ТОЛЬКО про линковку
Но тут бо́льшая диверсия (как во всей системе templates) это использование <> как скобок. Здесь спокойно хватило бы и круглых скобок.
Вообще то в языке С++ компайл тайм вещи передаются в < >, а рантайм вещи передаются в ( ), про sizeof не нужно заикаться, т.к. это C
темплеты C++ сильны, но крайне неудобны
Значит вы не умеете ими пользоваться, они крайне удобны
Все undefined behavior (UdB), от переполнения знаковых
Нет никакого другого способа разрешить компилятору хоть что то оптимизировать. Нет более хорошего пути чем введение УБ
Кому нахрен нужны именованные конструкторы? Функций не хватает?
Очень простые принципы нахождения подходящей функции - наиболее подходящая. Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.
Попробуйте почитать стандарт, там интересное:
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 взорвётся. Ещё одна радость.)
(Можно закостылить через фиктивный параметр конструктора, но почему не напрямую через имя?)
> Очень простые принципы нахождения подходящей функции — наиболее подходящая.
Да-да-да. С подстановкой неожиданных типов через неявные конверсии.
> Сложности возникают только когда вы наворотили нечто очень странное в коде. Проблема тогда не в принципе, а в коде.
Зависимость от чужого кода, где уже сотворено чёрт-те что и менять не получится — совершенно типовая ситуация. Странно, что вы с этим, судя по сообщениям, не сталкивались.
Мне тут объяснили в прошлом обсуждении про constexpr, что эта ерунда с constexpr вызвана не возможностями оптимизации, а необходимостью иногда вставлять в код отладочный вывод.
Хотя я бы на месте разработчиков стандарта добавил примитивы для constexpr-отладочного вывода вместо того чтобы разрешать constexpr иногда переставать быть constexpr.
struct {
int a;
int b;
} moo;
А как тебе такое, Илон Маск?
Когда в одной статье написано, что не нравится "Точка с запятой;" и "Медленная сборка", то у меня возникает логический диссонанс. Обыватели тоже цвет записывают скрытых вещей в достоинства/недостатки, но чтобы этот вопрос обсуждать...
Точка с запятой;
Да, это смешно и больше связано с моими фантазиями о каком-нибудь быстром питоне
Медленная сборка
Вот тут бы мне очень хотелось чтобы поскорее добавили работоспособные модули, пришлось бы писать меньше кода, а из-за их модульности можно было бы компилировать в многопотоке
Компилировать в несколько потоков можно уже сейчас
Модули в C++ существуют ещё задолго до появления первого компилятора для него, и называется это раздельная компиляция.
И неуча неспособного выучить даже простойшую точку с запятой никто не спрашивал. Общепринятым стандартом для всех зрелых языков программирования, проверенных десятками лет, оно стало вовсе не закрасивые глаза.
Ну я имел в виду работу лексера, потому что построить однозначную таблицу переходов проще (с единственным разделителем, в нашем случае ";"), а уже по таблице разбивать на лексемы намного быстрее и можно сократить количество проходов. Даже в многопроходном языке (C++, к примеру) явный и однозначный разделитель сильно сокращает количество символов для идентификации лексемы при разборе, что позволяет выполнять параллельно лексический и синтаксический разборы без всяких ухищрений.
Тем более использовать перевод каретки как разделитель выглядит странно и лишает возможности форматировать код так, как хочется (даже несмотря на то, что любая ДОС в терминале выполняет команды по кнопке "ввод").
Когда Страуструпа неустраивал C он написал C++. Предлагаю автору не скромничать и написать свой C+++ с модулями и системами сборки.
Перефразируя всемизвестную цитату:
"С++ сожет показаться ужасным языком программирования, если вы не пытаетесь написать что-то серьёзное на чем-то ещё".
Если тебе не нравится язык программирования, то просто не пиши на нем. Пиши на том, что нравится, публикуй кейсы, а не кликбейтные заголовки!
В результате основная часть «энтерпрайза» ушла много лет назад на Java, C#, математики — только подключают C++ код (а местами даже Fortran) через Python или R, почти все девопс-тулзы написаны на Go, и так далее.
Да, бо́льшая часть причин этого — неумение/нежелание морочиться с ручным управлением памятью (одно из самых неудобных и чреватых скрытыми ошибками), но часть аргументов автора статьи даёт вклад и в эти миграции.
Энтерпрайз ушел в Java и C# по большей части из-за того, что там стандартная библиотека включает в себя больше нужных вещей. До сих пор в плюсах нету стандартных классов для работы с networking, включая высокоуровневые протоколы, gui и так далее. А в те времена, когда энтерпрайз уходил на Java, в std не было даже thread'ов.
Ну и сейчас многих вещей нет в базовой поставке 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
Наверно, все от скуки и безделья, надо заняться делом.
Нигде нет идеала, мы ко всему подстраиваемся или меняем религию.
Я думаю об алгоритме и т.д., а не о точке с запятой :D
Но точка с запятой не влияет на алгоритм, только на C++
Много места в ОЗУ занимает эта точка с запятой, фигурная скобка? Палец устал?
Это как мем, где 500 разных арифмитических выражений без скобок в одном выражении, можно и так писать, но я для ясности буду делать осмысленные скобки.
Я после каждой закрывающей скобки блока (даже если там всего две строчки) делаю комментарий к чему она относится (на автомате) // if, // else, // while и т.д.
Код читабельнее. И ни как не напрегает.
А вот длинное reinterpret_cast. Согласен.
поразительный текст... у меня челюсть отвисла
буду считать что я его никогда не видел
Многострадальный C++ и системы сборки