Я хочу, чтобы вы задали себе один вопрос и честно на него ответили. Когда в последний раз вы получали настоящее удовольствие от программирования? Оглядываясь назад, я понимаю, что не испытывал подобных ощущений, наверное… уже лет десять. Удовольствия у меня не было ни от JavaScript, ни от Python, ни от Ruby или C — ни от чего. Когда я говорю «удовольствие» — я имею в виду ощущения человека, которого во время работы над неким проектом переполняет искренний восторг. Этот человек постоянно ловит себя на такой мысли: «Ох, ну какая ж круть. Поверить не могу, что моя безумная идея и правда сработала!».

Например, я писал маленькую игру-«рогалик». У меня была такая идея: «Готов поспорить, что у меня получиться воспользоваться этим вашим алгоритмом Дейкстры для соединения комнат при генерировании карты, сначала инвертируя карту, а потом его запуская. Вероятно, мне удастся прокопать отличнейшие туннели между комнатами». То было благословенное время, когда я пытался справиться с этой задачей, и при этом не чувствовал, что C++ мне мешает. Мне тогда удалось решить эту задачу, попутно многому научившись. Потом у меня появилась такая мысль: «Интересно, получится мне взять пользовательский интерфейс, сделанный на FTXUI, и просто напрямую его отрендерить в окно визуализации SFML?». Как и следовало ожидать, у меня всё отлично получилось. И хотя это было не так уж и сложно, я по ходу дела много узнал о том, как в C++ обрабатывается юникод. Ни одна из этих задач лёгкой не была, но все их, в принципе, можно было решить, и я не могу напридумывать себе достаточно много «подводных камней», которыми C++ мог бы помешать мне сделать то, что я хочу. Это — то, что я называю «удовольствием».

Я думаю, что дело тут, в первую очередь, в том, что программисты просто забыли, с какой целью они пришли в программирование. Я знаю, что я учился кодить не для того, чтобы помочь кучке миллиардеров стать ещё богаче. И не для того, чтобы заставить иммутабельный движок рендеринга вывести кнопку василькового цвета. И я, определённо, пришёл в программирование не для того, чтобы выслужиться перед несколькими «диктаторами», правящими каким-нибудь опенсорсным проектом.

Я начал программировать из-за того, что это было здорово и интересно. Помню, как не спал до четырёх утра, отчаянно пытаясь заставить свой отвратный код на gwBASIC вывести символ в консоль MS-DOS. Помню, как целыми неделями работал над странными GUI-идеями и над сетевыми серверами. И делал я это всё только потому, что у меня была какая-то идея. Вот ещё был случай — я на целый месяц ушёл в отладку, чтобы в итоге узнать о том, что причиной ошибки была тупейшая опечатка. И всё это — даже то, что расстраивало, всё это было просто невероятно кайфово! Это было, без сомнения, куда увлекательнее, чем что угодно другое из того, чему я учился.

И я совершенно определённо помню, что и C++ тоже был целым кладезем удовольствий. Даже в те дни, когда вокруг него было слишком много шума, и когда он, если честно, был ужасным языком. Классным он был из-за того, что я, вероятно, мог сделать на C++ всё что угодно. Тогда не надо было ждать, чтобы кто-то, получивший благословение от Python-иллюминатов, сделал бы некую ужасную обёртку вокруг кода проекта, написанного на C++.

Но… где-то пути к нашим дням C++ стал… скучным, растерял свою «кайфовость». И я полагаю, что эта тенденция до сих пор продолжается в отношении всех языков программирования. История «оскучнения» C++ может стать предостережением для других языков. А то, что произошло потом, может дать им надежду на светлое будущее. Я имею в виду великое воскрешение C++, которого никто не заметил.

Шаблонный метапокалипсис C++

С конца 1990-х и до начала 2000-х годов я активно использовал и C++, и Java — до тех пор, пока почти полностью не оставил C++ примерно в то же время, когда C++-сообщество занялось метапрограммированием.

I do metaprogramming.
Я занимаюсь метапрограммированием

Как бы там ни было, то, что нарисовано выше, отражает мои ощущения по этому поводу. Кучка идиотов нашла новый «углоскобочный» молоток шаблонного метапрограммирования и начала забивать им всё, похожее на гвозди, в радиусе 100 метров. Помню ожесточённые дискуссии с людьми, которые думали, что добавление пары чисел в шаблон было «быстрее», чем добавление тех же чисел прямо в код. И это — при том, что итоговый машинный код получался абсолютно одинаковым в обоих случаях.

Здесь сработали два фактора, которые серьёзно испортили репутацию C++ как в моих глазах, так и в глазах других людей. Первый — это одержимость шаблонным метапрограммированием, которое нашло чрезвычайно широкое применение. Второй — это то, что я воспринимал как странное высокомерие комитета по стандартизации C++, который отказывался сделать язык более удобным для программистов. C++ стал языком для «архитектурных астронавтов», которых больше интересовали идиома pimpl и метапрограммирование, чем забота о том, чтобы языком было бы приятно пользоваться.

Именно поэтому мне тогда и нравился язык Java. Компания Sun Microsystems, определённо, заботилась о программистах, и о том, что им было нужно. В Java был сборщик мусора! Язык Java, конечно, никогда не пойдёт по тому же пути, что и С++. Ни в коем случае. Java-программистов никогда не настигнет одержимость бредовым шаблонным кодом, паттернами AbstractFactoryFactory и Visitor. Они никогда не будут сильнее заботиться о стандартах, чем об удобстве использования языка. Никогда. <кхе-кхе>.

Полагаю, что это, вероятно, происходило примерно в то же время, когда многие люди моего возраста оставляли C++. Возможно, большинство из вас помнит переусложнённые многословные конструкции этого языка, перегруженные служебными символами вроде угловых скобок, <>, не дающими особых выгод.

Но C++ продолжил развиваться. Комитет по стандартизации, похоже, понял, что если он что-нибудь не предпримет — C++ превратится в малоизвестный язык, используемый лишь в одной отрасли экономики стоимостью в 10 триллионов долларов. Какая жалость!

Комитет достойно справился со своей задачей, разительно улучшил язык и реально сделал его снова интересным и приятным, не отступив при этом от исходных целей, стоявших перед C++. К сожалению — мало кто знает об этой огромной революции в C++. Поэтому я собираюсь привести ваши представления об этом языке к современному состоянию.

Невероятное возвращение C++ 11

У меня есть к вам просьба: посмотрите на то, что было добавлено в C++ 11. Пройдите по ссылке и взгляните на список потрясающих улучшений, которые появились в этой версии языка. Давайте обратим внимание на несколько самых масштабных изменений:

  • Ключевое слово auto. Да, в C++ действительно появилось ключевое слово для автоматического вывода типов. Я от этого пребывал в таком же шоке, как и вы, так как я помнил о том, что C++ был редкостной сволочью, которая без причины заставляла меня снова и снова вводить одно и то же проклятое слово.

  • Ключевое слово nullptr. И правда? Да — комитет исправил сишную гадость, касающуюся странного и загадочного определения NULL. Это — и ноль, и не ноль, так как какой-то чудак из комитета по стандартизации C всё ещё пишет код для PDP-11. Там нет нулевых адресов, а слова там имеют длину в 13,5 бит.

  • Цикл for, основанный на диапазоне значений. Вы же это и правда читаете? И вы всё прочли правильно — в C++ имеются итераторы и конструкция вида for(x in list), которые есть и в Python. И что самое приятное — всё это работает с ключевым словом auto, поэтому можно перебирать списки, не заботясь о подборе правильного типа. JavaScript всё ещё даже не может себе уяснить то, что такое цикл for, а в C++ всё сделано как надо.

  • Лямбда-выражения. Что? Да, так и есть — в C++ есть лямбда-выражения, и это — не хлам, похожий на то, что присутствует в Python. Лямбда-выражения в C++ удивительно хороши. Я полагаю, что они, в итоге, способны очень сильно изменить подход программистов к проектированию программ. Посмотрите, как их использует FTXUI, чтобы увидеть то, как сильно они повысили удобство использования разных API.

  • Заголовок <chrono>. Вы представляете — что произошло в C++ в 2011 году? В языке появилась библиотека для работы с датой и временем. Её возможности превосходят то, что имеется практически во всех языках, которыми я когда-либо пользовался. Эта библиотека настолько качественно сделана, что можно просто написать 100ms и создать сущность, представляющую 100 миллисекунд. И да — это — корректный типобезопасный синтаксис.

  • Заголовок <regex>. И тут нет ошибки — в C++ имеется встроенная библиотека для работы с регулярными выражениями, которая, как и прочие новшества, получилась просто замечательной. Её единственный изъян в том, что она работает не точно так же, как похожий механизм JavaScript в режиме ECMAScript, но в остальном никаких претензий к ней нет.

  • Умные указатели unique_ptr и shared_ptr. С их помощью можно реализовать различные схемы подсчёта ссылок и применять различные практики управления владением и жизненным циклом объектов в памяти. После того, как в языке появились эти указатели, программисты, в основном, перестали массово применять операции по выделению памяти в куче. Теперь они всё чаще отдают предпочтение стеку, и там, где это нужно, используют лишь эти указатели.

  • Заголовок <thread>. Да! Многопоточность теперь встроена в сам язык. Что это за язык? Прежний C++, помнится, говорил программистам : «Нужны потоки? Пишите их сами, тупицы». А вот слова нового C++: «Да! Потоки — это очень хорошо. Вот вам потоки».

Я, честно говоря, не знаю о том, что случилось, или о том, почему в C++ 11 появилось так много улучшений. Но то, что произошло с языком, напоминает мне выход стандарта ES6 в мире JavaScript. Это — полный пересмотр философии и стиля языка без отказа от прежних подходов к решению задач.

Да, в C++ это появилось… вроде как

Правда, гораздо важнее то, что теперь C++ — это более современный язык, в котором имеется довольно много такого, что можно найти в других языках. Вот список кое-каких распространённых возможностей, которые могут понадобиться современному программисту:

  • Надо обойти файловую систему? Обратите внимание на filesystem.

  • Нужна система управления пакетами? Попробуйте Conan, WrapDB из системы сборки ПО Meson и vcpkg.

  • Возникла необходимость в матричных вычислениях? Возьмите Eigen.

  • И, чтоб вы знали, платформа Tensorflow написана на C++.

  • Вас интересует работа с графикой? В общем — не знаю — надо ли мне упоминать о том, что огромнейшее количество игр написано на C++? Тем, кого интересует графическое применение C++, рекомендую начать с библиотеки SFML.

  • Требуется создать графический пользовательский интерфейс? Что сказать — могу предположить, что в вашем распоряжении имеются Qt и wxWidgets. И существует ещё одна хорошая библиотека — ImGui. А моя любимая библиотека для создания интерфейсов — это…

  • Нужен интерфейс для терминала? Полагаю, что, на каком бы языке вы ни писали интерфейсы командной строки, библиотеки, лучше, чем FTXUI, вам не найти. Она замечательно спроектирована. Это — просто замечательный проект. Правда, подходит FTXUI только для терминальных интерфейсов.

В C++ имеется практически всё, что может понадобиться современному программисту. Но, если говорить честно, качество того, что можно найти в экосистеме C++, может порадовать, а может и нет. Я сказал бы, что качество разных C++-инструментов обычно выше, чем можно ожидать, и оно выше, чем качество тех инструментов, которые мне попадаются в сферах JavaScript и Python. Полагаю, если не лукавить — справедливо будет сказать, что нестабильное качество инструментов — это характеристика, применимая к практически каждому из существующих языков. Скажите — вы правда думаете, что система управления пакетами в Python — это первоклассный инструмент? Правда так думаете? А почему тогда существует штук 10 менеджеров пакетов для Python? Вы правда думаете, что хорош абсолютно любой API для GUI? А как насчёт API для работы с файловой системой? Я тут о том, что, если хорошо присмотреться, во всём этом полно недостатков. Поэтому я сказал бы, что в C++ всё почти так же, как в других языках, но большинство инструментов здесь чуть лучше «средней температуры по больнице». Есть здесь и прямо-таки превосходные проекты — вроде FTXUI. И стандартная библиотека C++ тоже очень хороша.

Всё это так, но говорилось об «удовольствии от программирования»

Слышу вас. «Да, конечно, но в Rust имеются реально качественные веб-серверы… и… и — структуры данных, и… что ещё… хорошая стандартная библиотека. Программировать на Rust так же приятно, как и на C++!». Да, но моя точка зрения состоит не в том, что удовольствие от программирования на C++ обеспечивается всеми вышеперечисленными новшествами. Смысл моих высказываний, до сих пор, заключался в следующем:

Вы не правы, если считаете, что С++ — это древний и неповоротливый язык, набитый угловыми скобками, указателями и прочим мусором вроде this->.

Я полагаю, что C++ — это, возможно, самый конкурентоспособный из существующих в наши дни языков, с большим отрывом обходящий другие языки. Не думаю, что мне может встретиться какая-то задача, которую я не смогу решить с помощью C++. Более того — практически всё, что мне может понадобиться, может быть реализовано множеством способов:

  • Если мне хочется написать настольное приложение — я могу сам написать его код, воспользовавшись чем-то вроде fenster. Ещё я могу прибегнуть к SFML, или использовать QT, или могу олдскульно, без подобных библиотек, написать всё с нуля (взгляните на документацию к fenster — там вы найдёте хорошее руководство по такого рода делам).

  • Если мне надо воспроизводить звуки — к моим услугам имеется бездна вариантов — от опенсорсных, до платных, включая самые разные «самоделки». Я могу, чтобы наспех что-нибудь соорудить, взять SFML, а могу заплатить за wwise и тогда в моём распоряжении окажется самый лучший инструмент для работы со звуком.

  • Если мне понадобится работать с 3D-графикой — я могу обратиться к OpenGL, Vulkan, Direct3d, Ogre3d, а так же — ко многим другим подобным проектам. А вообще — 3D-библиотеки для C++ встречаются буквально на каждом шагу.

  • Если мне нужна будет математика — я смогу найти всё что угодно — от BLAS и Eigen до библиотек вроде GMTL, заточенных под игры.

  • Необходимо работать с искусственным интеллектом? Напомню, что платформа Tensorflow написана на C++.

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

И это — лишь несколько примеров. Самое важное — понять, что С++, на самом деле, не мешает программисту. У С++-программиста есть прямой доступ к практически любой библиотеке через ABI C или C++, и прямой доступ к каждой из существующих операционных систем. Поэтому он способен сделать самостоятельно практически всё, что ему может понадобиться.

С++ абсолютно всё равно

Ну ладно, на C++ можно сделать всё, что душе угодно, а комитет по стандартизации C++ в 2011 году откорректировал курс развития языка, нацелив его на производительность труда программистов. И в чём именно тут заключается «удовольствие от программирования», господин Зед А. Шоу?

C++ находится в таком фантастическом творческом «месте силы», где сам язык и его экосистема отличаются исключительно высоким качеством, но сам язык при этом не достаточно популярен для того, чтобы привлечь к нему тех ненормальных, которые поганят языки.

Вы знаете — о ком я говорю. Помните, как я писал о том, что оставил C++ из-за того, что кучка странных людей сходила с ума по паттернам проектирования и по шаблонному метапрограммированию? Потом я пошутил о том, что то же самое случилось с Java — там были паттерны проектирования и безумные XML-файлы. Дальше — то же самое случилось с Ruby — с того момента, как стал популярным фреймворк Ruby on Rails. Этот список пополнили Python, JavaScript и Rust. Каждый раз, когда некий язык становится популярным — на него налетает стая несносных идиотов, которые губят этот язык.

Как видите — C++ сейчас не в моде, поэтому все эти вредители не обращают на него внимания. C++ настолько далёк от какой-бы то ни было фешенебельности, что его ненавидит даже Белый дом. Поэтому все надутые любители командовать, которые вьются вокруг, тыча всех носом в стандарты, перебрались на языки, где могут разбрасываться своей ерундой и инфлюэнсерским мусором.

Это — те самые люди, которые орут: «Почему не пишешь на Rust?!». Они — те, которые стыдят других: «Почему не используешь React?!». Это они надрывают глотку: «Почему так много unsafe в Rust-коде?». Именно им принадлежат такие тирады: «Серьёзно? Цикл for в Ruby? Да чтоб тебя! Неуч!». К нашему счастью — C++ недостаточно популярен для того, чтобы всё это жульё могло бы на нём заработать, поэтому оно его, в основном, игнорирует.

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

Всё это значит, что C++ — языку программирования, и сообществу, которое сложилось вокруг C++, просто абсолютно наплевать на то, чем занимается каждый конкретный программист. Хотите писать библиотеку для матричных вычислений? Пишите. Хочется сделать GUI-библиотеку? Вперёд. Появилось желание создать игровой движок? Устройтесь на диване тёмной ночью и действуйте. Придумалось соорудить странную игру, которая наблюдает за вашим кодом и над вами издевается? На самом деле — всем всё равно, и большинству покажется, что это — либо забавно, либо прикольно.

«Абсолютно всё равно» — это важнейшее условие для творчества

«Но Зед, разве нам не нужен Великий блистательный лидер, который скажет — как правильно поступать? Разве то, что в C++ нет Милостивого пожизненного диктатора, не лишает нас надежды найти верный путь? Разве вы не совершаете… осмелюсь заметить… ошибок? Как вы можете выжить без свиты Диктатора, которая вас позорит, заставляя бесплатно на неё работать?»

Я, конечно, не имею в виду, что кто-то мне скажет что-то подобное, но уверен, что у моего читателя крутится в голове что-то на эту тему. Такой настрой — это одна из главных причин, по которой другие языки — это не про удовольствие. Другие языки строго контролируются организациями, которые агрессивно бросаются на всех, кто пытается сделать что-нибудь классное. Если вы пытаетесь сделать проект, конкурирующий с тем, на котором зарабатывает Диктатор — тогда будьте готовы к тому, что вас будут годами, так сказать, «травить собаками».

Я твёрдо верю в то, что для того, чтобы дать расцвести творчеству, нужно иметь возможность заявить о своей идее без страха критики или осуждения. Нужно иметь возможность выразить себя, и уже после того, как идея, кричащая и размахивающая руками, родится, отойти на некоторое расстояние и критически на неё взглянуть. Правда, после того, как идея воплощена, лучше всего, если именно вы станете её первым критиком. Именно этот шаг от «творческой свободы от критики» к «сосредоточенности на качестве» и создаёт условия для выдающегося креативного творчества.

Эту концепцию лучше всего можно объяснить, приведя пример с учителем рисования, который у меня когда-то был. На самом деле, её объясняет поведение всех моих учителей рисования. Они подходили к неоконченной работе и пытались её исправить. Они, что досадно, становились не туда, куда надо. Получается, что они видели работу с высоты в 5 футов, а я её видел с высоты 6 футов и 2 дюйма. Они говорили, что рисунок у меня неправильный, несмотря на то, что перспектива, в которой его видели, совершенно отличалась от моей. Или они комментировали какие-то аспекты неоконченной работы, которые тяжело судить из-за того, что они пока не показаны в составе всего рисунка.

Ошибка, которую допускали эти учителя, заключалась в следующем. Они полагали, что «ошибка», возникшая на раннем этапе, не может быть исправлена в процессе работы, и так и останется «ошибкой» в готовом рисунке. Вполне возможно, что то, что в итоге создаст ученик, окажется, после завершения работы, вполне приемлемым. А пока рисование не окончено — ничто не выглядит правильным, поэтому бессмысленно пытаться это «исправлять».

Есть один важный момент, связанный с творчеством, который не понимали эти учителя. Всё выглядит и воспринимается как ерунда на «уродливой промежуточной» стадии. В самом начале всё кажется просто великолепным, но по мере продвижения работы в неё пробирается беспорядок: чтобы достичь итогового видения идеи, всё в ней перемешивают и перестраивают. Это перемещение от задумки к финалу больше похоже не на вождение автомобиля, а на хождение по канату, когда творец балансирует на тонкой линии, пытаясь достичь цели. Единственный способ научиться ходить по канату — снова и снова падать с него — и так до тех пор, пока учащийся не обретёт равновесие.

Вокруг многих других языков, похоже, сложилась такая ситуация, когда «доброжелатели» постоянно пытаются, на полпути, столкнуть программиста с каната, и продать ему вместо каната автомобиль.

Почти бесконечное пространство для творчества, которое даёт нам C++, в сочетании со сравнительно небольшим количеством «блюстителей чистоты», позорящих других, переполняет творчеством программирование на этом языке. Если у меня есть идея — меня ограничивают лишь мои знания и навыки, а не рамки самого языка и не мнение неких выпендрёжных инфлюэнсеров, которые объявляют, что я неспособен преподавать, делая так из-за того, что мне не нравится их Милостивый пожизненный диктатор.

Справочник cppreference.com — это просто чудо

Думаю — сайт cppreference.com — это пример самой лучшей документации по языку программирования, которой мне когда-либо доводилось пользоваться. Здесь есть практически всё, что, как мне кажется, должно быть в документации по языку. Ниже я скажу о том, чего там не хватает, а пока — вот список того, что мне там нравится:

  1. К каждому ключевому слову и к каждой библиотеке имеются полные справочные материалы.

  2. В статьях документации, посвящённых различным аспектам языка, кроме того, имеются обширные работающие примеры. Я так думаю, что из тех примеров, что я пробовал, лишь около 5% работали не так, как ожидалось. Большая часть этих неувязок произошла не из-за ошибок в тексте документации, а из-за особенностей реализации используемого мной компилятора.

  3. Каждая возможность языка снабжена комментарием по поводу версии стандарта C++, в котором она появилась. Кроме того — в статьях имеются и ссылки на конкретные стандарты, имеющие отношение к той или иной возможности, а так же — на справочные материалы, относящиеся к стандартизации.

  4. На сайте есть строка поиска, основанная на поисковой системе duckduckgo. Поэтому, когда там что-то ищешь — успеху не повредят ни ошибки в написании ключевых слов, ни ситуации, когда не вполне точно помнишь какие-то термины.

  5. В дополнение к полным и понятным справочным материалам, к очень хорошим примерам и к точным сведениям о версиях языка, на сайте имеются подробные описания важных концепций с примерами. Взгляните на справочную статью Copy elision, посвящённую пропуску лишних операций копирования. Эта статья, хотя она и рассчитана на «технарей», написана очень хорошо, и включает в себя множество примеров, иллюстрирующих то, о чём в ней идёт речь.

Единственное, чего не хватает на cppreference.com — это сведений о том, как устанавливать различные компиляторы на разных платформах. Я не могу окончательно для себя решить — нужно или нет администраторам сайта размещать на нём подобные вещи. Для других языков документация такого рода очень важна. Но в случае с C++, полагаю, подобное может столкнуться с недовольством со стороны некоторых из членов сообщества (*кхе-кхе* Microsoft) и, возможно, поэтому такие сведения на сайт и не добавляют. С другой стороны — учитывая то, что компиляторов C++ и платформ не так уж и много, на cppreference.com вполне можно добавить руководства по началу работы в различных средах.

С++ — это не «праздник каждый день»

Хотя я и испытываю огромное наслаждение, когда пишу на C++, мне хотелось бы, чтобы вы понимали, что я ни в коем случае не думаю, что этот язык совершенно лишён изъянов. Он и близко не может называться идеальным. Я так думаю, что, во-первых, C++ сейчас практически так же хорош, как любой из других существующих языков, и — во вторых — я думаю, что ни один язык не идеален. Свои неувязки есть в C++, и точно так же — есть они и в любом другом языке. Просто проблемы C++ отличаются от проблем других языков.

Например — в C++ крайне тяжело заставить работать в Windows установщик, сделанный не Microsoft. И да — установщик MSYS2 не работает. MSYS2 — это полный отстой, поэтому не надо мне ничего о нём говорить. Мне пришлось создать целую кучу PowerShell-скриптов только для того, чтобы облегчить установку компиляторов и инструментов разработки. Если вы полагаете, что это — просто ужас — тогда почему вы пользуетесь Python? Если вы попытаетесь установить любой Python на Windows — вам придётся иметь дело с тем, что установщик не добавляет Python в PATH… только если это — не благодатный установщик от Microsoft или ActiveState. А таким установщикам позволено добавлять Python в PATH на Windows.

Как видите — тут C++ находится в выигрышном положении, так как нам не приходится разбираться с какими-то «тайнами мадридского двора», которые позволяют Microsoft и ActiveState нарушать явный «закон», запрещающий установщикам Python использовать PATH. Одно только это с моей точки зрения — огромный плюс. И меня не радует необходимость споров с идиотами, которые думают, что это — нормальная ситуация для Python.

Если вы считаете, что ваш язык лучше других — дайте мне что-то около часа, и я найду в нём достаточно гадостей. В свете этих находок ваш язык будет выглядеть как жалкое подобие чего-то такого, что можно назвать «удобным». Проблемы есть во всех языках, дело лишь в том, что недостатки C++ не портят удовольствия от работы с ним.

Вот несколько проблем, с которыми я сталкиваюсь в современном C++:

  • Более 95% сообщений об ошибках компилятора С++ выглядят гораздо хуже, чем сообщения об ошибках всех остальных языков. И я тут даже не преувеличиваю. Сообщения об ошибках в C++ хуже всех. Полагаю, если комитет по стандартизации языка захочет принести языку огромную пользу — он создаст стандарт для сообщений об ошибках, так как в настоящее время эти сообщения вполне можно назвать «почти вредительскими».

  • Инструменты для сборки проектов — это просто ужас. Я совершенно не понимаю — почему так тяжело пользоваться инструментами сборки C++-проектов, и почему люди, которые их создают, принимают наиглупейшие решения по поводу этих инструментов. Лучшее, что я до сих пор обнаружил в этой сфере — это сборщик Meson, но и он полон последствий идиотских решений. Например — это игнорирование директив, касающихся установленных пакетов, и использование неисправных системных библиотек без предупреждения. Я наткнулся на эту проблему в день, когда писал эту статью, и меня это просто взбесило.

  • У производителей компиляторов нет стимула следовать стандартам. Я говорил о том, что в Clang имеется ошибка в std::source_location, и это — отличный пример тех проблем, с которыми мы все иногда сталкиваемся. Я твёрдо уверен в том, что комитету по стандартизации языка нужно начать публиковать список компиляторов, в которых не исправлены ошибки, подобные этой. Нужна простенькая страничка, на которой содержится список компиляторов и сведения о том, насколько они соответствуют стандарту. Там же должны быть особо упомянуты компиляторы, в которых имеются очевидные ошибки. Это принесёт огромную пользу.

  • Язык чрезвычайно сложен из-за его истории. В С++ имеется масса устаревших конструкций, через которые приходится продираться для того чтобы найти что-то ценное. У меня возникает такое ощущение, что это похоже на то, как если бы C++ был бы тремя языками (или ещё большим их количеством), похожими на JavaScript. Есть старый стиль, где память практически для всего выделяется в куче, и где почти везде используются обычные указатели. Далее — есть стиль «Шаблонного метапокалипсиса». И имеется ещё стиль «пост-2011», где память почти под всё выделяется в стеке и где очень мало указателей. А ещё я вижу более новый стиль, где все, вместо class, используют struct, и не особенно часто прибегают к наследованию. Путешествие по всем этим «эрам» C++ любого может сбить с толку, а на YouTube имеется немало знатоков программирования, которые, похоже, застряли в самых разных эпохах языка.

  • Я люто ненавижу RAII. Я встречаюсь с множеством ситуаций, где RAII мешает решать настоящие задачи инициализации сущностей. Причём, мешает настолько, что эта «возможность» C++ становится больше похожей на какой-то сбой, а не на что-то реально полезное. Я считаю (учитывая то, как много других языков этой возможности лишены, никак от этого не страдая), что С++ может пойти на пользу некий промежуточный вариант. Это может быть нечто среднее между текущим подходом, использующим RAII, и тем, где применяются конструкторы инициализации объектов, которые имеются в других языках.

Но всё это — тема для другой статьи. Сейчас мне лишь хотелось бы выразить то, сколько удовольствия мне доставляет написание всяких глупостей в C++, а так же — хотелось бы показать другим людям то, что C++ — это не то, что они себе представляют. Если вы когда-нибудь встречались с учебным курсом, в котором пытаются ругать C++ за обилие угловых скобок — просто знайте, что автор этого курса — круглый невежда, сам не понимающий того, о чём говорит. Да вот, например — посмотрите на этот код разметки пользовательского интерфейса из FTXUI:

  document_ = Renderer([&]{
    return hbox({
        hflow(
          vbox(
              text(format("HP: {}", player_.hp)) | border,
              text(status_text_) | border
              )  | xflex_grow
        ),
        separator(),
        hbox(map_view_->Render()),
    });
  });

Скажите честно — если бы вы не знали о том, что это — C++, смогли бы вы узнать язык, на котором это написано? Мне больше нечего добавить.

О, а приходите к нам работать? ? ?

Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.

Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.

Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.

Присоединяйтесь к нашей команде

Комментарии (34)


  1. Jijiki
    20.01.2025 09:45

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

    второй раз прифигел когда обнаружилось что нормали можно скейлить (кучность), и почти за дешево знать коллизию с террейном, после расчета (там с формулами еще есть) - радости тоже не было предела

    математика в 3д интересна, почти весь мат аппарат буквально в базе лежит, чтобы отрисовать нужны векторы, матрицы, кватернионы. тоесть можно пойти от визуала не думать о математике, но потом можно столкнуться что там всё это число

    про шаблоны тоже заметил, нужен баланс шаблонов/не шаблонов


  1. DjUmnik
    20.01.2025 09:45

    Кайф был бы, если бы фичи новых стандартов реализовывались вовремя всеми компиляторами. Что там с модулями?


  1. segment
    20.01.2025 09:45

    Скажите честно — если бы вы не знали о том, что это — C++, смогли бы вы узнать язык, на котором это написано? Мне больше нечего добавить.

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


    1. rukhi7
      20.01.2025 09:45

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

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


      1. vadimr
        20.01.2025 09:45

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


        1. rukhi7
          20.01.2025 09:45

          Причём она прямо следует из принципа инкапсуляции

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


          1. vadimr
            20.01.2025 09:45

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


  1. N_lible
    20.01.2025 09:45

    Согласен с тем что С++ это офигенный ЯП. Изучение программирования начинал на Python, думал на нём же и буду работать. Как мне кажется я его не плохо изучил но никуда не брали а параллельно работал в асу и писал pet проектики на python. Пока по приколу не откликнулся на вакансию С++/qt в своём провинциальном городке и меня с нулевым опытом на С++ взяли "попробовать". Прошло 2 года и я не собираюсь слазить с этого языка, python теперь для чего нибудь быстренького, маленького проектика. А вот С++ нужно закатывать рукава и получать удовольствие. Так что да, С++ это чума!)


  1. vadimr
    20.01.2025 09:45

    Не понял, почему автор считает, что лямбды в C++ лучше, чем в питоне? Хотелось бы какой-нибудь аргумент.

    На мой взгляд, в питоне – нормальные классические лямбды из ФП, а в C++ этим словом называется какое-то дитя противоестественной связи ФП и ООП.


    1. Jijiki
      20.01.2025 09:45

      на сколько хватает моих скромных знаний лямда - это анонимный класс на месте вызова лямбды подставляется адрес того класса который вызываем, из нескольких лямбд можно сделать +- нужное навесное поведение, в своё время мне такое ДжиПиТи подсказал я изучил тему и запомнил, тоесть грубо говоря можно квесты лепить на лямбдах тоесть регистрировать в вектор обработчика список квестов и гдето во время исполнения чтото проверять, по-сути лямбда тоже functional программирование тоесть можно и в std::functional засунуть могу пример показать если интересно

      class ValidRules {
      public:
        int id;
        std::function<bool()> getRule;//everyRule/everyQuest
        const char* message;
      };
      bool checkSomethink(CGame<Component<C*>> Somethink,std::vector<int> pT,int c);
        //table of quests
        std::vector< ValidRules  > validQuests = {
          {0, [&]() -> bool { return checkSomethink(Somethink); }, "Message"}
        };


      1. vadimr
        20.01.2025 09:45

        Всё так и есть в C++. Но в функциональном программировании лямбда – это просто обычное тело функции (анонимное, поскольку [пока] оно не присвоено в качестве значения какому-то имени). Так и в питоне, оператор def для объявления обычной функции является просто синтаксическим сахаром для оператора присваивания, в правой части которого находится лямбда (тело функции).

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

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


        1. Jijiki
          20.01.2025 09:45

          ну например вот я смотрел туториал "Creating a Voxel Engine (like Minecraft) from Scratch in Python" давно еще, там крутые штуки питона используются, но по итогу мощи не хватало и он врубал jit, а по самому питону не скажу


        1. domix32
          20.01.2025 09:45

           а вот почему автор считает, что лямбды в С++ лучше лямбд в питоне – для меня загадка.

          мне кажется довольно очевидным, что в питоне писать лямбды сложнее lamda x: x + 1 смысла нет, ибо код преватится в лапшу заметно быстрее, чем то же произойдёт в С++. Ещё и PEP-ом потом тыкать станут, мол не там пробел, не там запятая, плюс захват контекста имеет свои нюансы.


    1. zzzzzzerg
      20.01.2025 09:45

      Думаю, что автор имеет в виду проблемы с захватом переменных замыканиям в циклах в Python. В С++ можно выбрать разные режимы захвата, в Python приходится выкручиваться. Ну и надо делать ссылку на авторский стиль.


      1. vadimr
        20.01.2025 09:45

        Так ведь эти режимы захвата придуманы только из-за того, что в C++ нет сборки мусора и надо руками регулировать время жизни объектов. В питоне лямбда образует замыкание со всеми лексически видимыми переменными, так что всё работает автоматически.


        1. zzzzzzerg
          20.01.2025 09:45

          Значит про проблемы вы немного не в курсе.


          1. vadimr
            20.01.2025 09:45

            Возможно. Вот и хотелось бы узнать.


            1. zzzzzzerg
              20.01.2025 09:45

              1. vadimr
                20.01.2025 09:45

                Ну это известная сложность у людей, изучающих питон после C++, но это не проблема питона и тем более не проблема программирования вообще. Именно так и работает лексическое замыкание в теории и так же оно работает и в питоне. Переменная i здесь относится к главной программе. Если автор вопроса хотел сохранить её значение на момент вызова, то и должен был сделать это (например, заведя формальный параметр i у лямбды).

                Точно так же будет работать и обычная, именованная функция. В соответствии с лексической областью видимости имён.


                1. zzzzzzerg
                  20.01.2025 09:45

                  Вы наверное что-то мне пытаетесь доказать, но я не понимаю зачем. Хотите поспорить - напишите автору (автор в отличие от нас с вами написал свою первую книгу по питону 10+ лет назад).


                  1. vadimr
                    20.01.2025 09:45

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

                    Вы предположили, что он имел в виду случай со stackoverflow, как причину своего утверждения о преимуществе лямбд в C++. Но это нелогично, потому что именно лямбды в питоне работают в данном случае теоретически правильным и ожидаемым непредвзятым пользователем образом. Откуда бы человек вообще стал предполагать, что значение переменной i каким-то образом вдруг зафиксируется, если только он раньше не сталкивался с таким поведением в C++? Общее правило состоит в том, что переменная имеет в каждый момент единственное значение в своей области видимости.


                    1. zzzzzzerg
                      20.01.2025 09:45

                      Они работают ожидаемо, если люди понимают, что такое лексическое замыкание и позднее связывание. У нас на собесах 8 из 10 человек про это не знаю, а код такой писать иногда приходится (чуть сложнее, чем передать индекс в лямбду) и рецепты надо помнить. Ваше высказывание про логично/нелогично приводит меня к мысли, что вы с подобным не сталкивались. Люди вон считали это багами - Issue 13652: Creating lambda functions in a loop has unexpected results when resolving variables used as arguments - Python tracker .

                      В С++ аналогичной проблемы не будет если лямбда связывается по значениям, а не по ссылкам.


                      1. vadimr
                        20.01.2025 09:45

                        Ваше высказывание про логично/нелогично приводит меня к мысли, что вы с подобным не сталкивались.

                        Посчитал сейчас статистику по последней программе, за которую получал деньги – 2694 строки, среди них 69 лямбд. Это достаточно для того, чтобы считать, что я сталкивался с лямбдами, или должна быть плотность функционального программирования больше?

                        позднее связывание

                        Время связывания вообще не имеет отношения к обсуждаемому вопросу.

                        У нас на собесах 8 из 10 человек про это не знаю

                        Охотно верю, но надо просвещать людей.

                        Люди вон считали это багами

                        Ну так привыкли к C++.


                      1. zzzzzzerg
                        20.01.2025 09:45

                        Ну посчитайте сколько у вас лямбд внутри цикла.

                        Позднее связывания к этом относится напрямую.


                      1. vadimr
                        20.01.2025 09:45

                        Да причём здесь связывание? Позднее связывание говорит только о том, что связывание производится по мере обращения к значению. Но само значение переменной i в вашем примере в любом случае к моменту вызова функции выходит за цикл, независимо от того, рано произошло связывание с ним или поздно.

                        Оттого, что произошло лексическое связывание, само значение же не прекращает меняться во внешней программе.

                        Связывание и захват значения в C++ – это разные вещи.

                        Позднее связывание играло бы роль, если бы вы после цикла эту переменную i вообще переопределили другим описанием (хоть это и нетипично для питона).

                        Например, вот это – позднее связывание:

                        i = 1
                        x = lambda : i()
                        
                        def i ():
                          return 2
                        
                        print (x())

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

                        А вот в такой программе:

                        i = 1
                        x = lambda : i
                        i = 2
                        
                        print (x())

                        – наплевать, раннее связывание с i или позднее, всё равно значение i в момент вызова лямбды равно 2, потому что никакой другой переменной i нет.


                      1. zzzzzzerg
                        20.01.2025 09:45

                        Вот вам два примера:

                        lambdas = []
                        for idx in range(10):
                            idx_copy = idx
                            lambdas.append(lambda : print(idx_copy))
                        
                        for l in lambdas:
                            l()
                        using System;
                        using System.Collections.Generic;
                        
                        
                        namespace HelloWorld
                        {
                        	public class Program
                        	{
                        		public static void Main(string[] args)
                        		{
                        		  var lambdas = new List<Action>();
                        		  
                        		  for (var idx = 0; idx < 10; idx++)
                        		  {
                        		    var idx_copy = idx;
                        		    lambdas.Add(() => Console.WriteLine($"{idx} -> {idx_copy}"));
                        		  }
                        		  
                        		  foreach(var l in lambdas)
                        		    l();
                        
                        		}
                        	}
                        }

                        Выбрал C# потому что ранее вы говорили про сборку мусора.


                      1. vadimr
                        20.01.2025 09:45

                        Я не знаток C#, но здесь у вас разница не во времени связывания, а в том, что в первом случае у вас используется обычная функциональная лямбда, т.е. просто записанное безымянное тело функции, а во втором – в цикле создаётся новый объект, содержащий функцию. Имеющий в том числе и свою собственную память.

                        Вот это выражение, как я понимаю, преобразуемое к Action:

                        () => Console.WriteLine($"{idx} -> {idx_copy}")

                        – конструктор объекта.


  1. dmitry_rozhkov
    20.01.2025 09:45

    А есть хорошая C++ библиотека, реализующая Communicating Sequential Processes типа горутин с каналами?


    1. Kelbon
      20.01.2025 09:45

      в С++ есть выбор, стеклес или стекфул корутины,

      стеклес:
      https://github.com/kelbon/kelcoro
      стекфул:
      userver (фреймворк) или boost context и тд


  1. AdrianoVisoccini
    20.01.2025 09:45

    Когда в последний раз вы получали настоящее удовольствие от программирования? 

    Честно? Вот как после того как забросил программирование именно из-за С++ который вверг меня в настоящую депрессию и после огромного перерыва в 10 лет сел писать на Java, пя получаю удовльствие КАЖДУЮ МИНУТУ КАЖДУЮ КАРЛ
    ОНО САМО ВСЕ ПИШЕТ, ВСЕ РАБОТАЕТ, ПАМЯТЬ ЧИСТИТ САМО, ЛОМБОК ВСЕ САМ ГЕНЕРИРУЕТ ЗА ТЕБЯ
    я сажусь писать код с улыбкой, пишу и хохочу вслух как раз как клоун Роксо из вашего поста. Боже, писать не на С++ это такое наслаждение, не сравнимое не с чем в мире. Как говорится - дай человеку козу, чтобы он понял как хорошо жилось без козы, так же и у меня с С++


    1. JKot
      20.01.2025 09:45

      А я как автор, начинал с плюсов, ушёл в java/go/c# лет на 10 и по итогу вернулся к плюсам и неистово кайфую. В общем каждому своё.


      1. segment
        20.01.2025 09:45

        А какой в итоге код получается в production на плюсах? Сколько ни смотрел на большие проекты, видел в основном более процедурный стиль (что-то близкое к Си с классами), ну там немного обмазано умными указателями и несколько стандартных контейнеров. На C# как раз можно писать используя его сахар и все остается понятным, но на плюсах как получается?


  1. feelamee
    20.01.2025 09:45

    это перевод..

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

    Раст хорош, все это знают


  1. Nuflyn
    20.01.2025 09:45

    dr rockso: i do C++