Обзор книги «С++ 20 в деталях»: доступно, но не для джунов
Меня зовут Дмитрий Луцив, я работаю в СПбГУ на кафедре системного программирования и в лаборатории компании YADRO, веду ряд IT-дисциплин на математико-механическом факультете и помогаю вузам актуализировать образовательные программы под задачи индустрии, как сотрудник группы образовательных инициатив YADRO.
Я давно не программировал на С++, но стараюсь не отставать от изменений, происходящих в языке. Книга «C++ 20 в деталях» заинтересовала меня личностью автора (на секунду, это Райнер Гримм) и возможностью познакомиться с новым источником знаний о «плюсах». Решил её прочитать, чтобы понять, кому материал будет полезен, и могу ли я его рекомендовать. В этой статье я расскажу, что в книге хорошего, а что можно было бы сделать лучше, а также дам список дополнительных источников для специалистов разного уровня.
Краткое содержание
«С++ 20 в деталях» — перевод на русский язык книги Райнера Гримма «C++ 20: Get the Details». Перевод и научную редактуру выполнили мои коллеги, преподаватели МИЭМ ВШЭ.
Книга начинается с экскурса в историю языка С++. Затем автор делает краткий обзор новых возможностей C++20: концептов, модулей, новой библиотеки диапазонов, корутин, оператора трехстороннего сравнения, нововведений в стандартной библиотеке и в поддержке параллельности.
Следом идет несколько глав, в которых темы раскрываются подробно.
- Концепты. При обобщенном программировании типы и функции можно параметризовать во время компиляции другими типами или значениями. Эти параметры, в свою очередь, тоже должны соответствовать каким-то требованиям. Концепты позволяют обеспечить это, задавая условия на параметры обобщенных классов и функций. Это довольно мощная возможность. Концепты напоминают классы типов в языке Haskell, известном строгостью типизации.
- Модули. Позволяют минимизировать или исключить использование препроцессора и ускорить сборку больших программных проектов.
- Оператор трехстороннего сравнения. Позволяет компактно задавать упорядочивание пользовательских типов, аналогично cmp из Python или функциям сравнения для C, но реализованный строго в плане типов.
- Назначенная инициализация. Это изменения и дополнения, которые касаются возможностей инициализации составных и контейнерных типов в коде программы. Очень не хватало, если сравнивать со многими другими языками!
- consteval и constinit. Конструкции, фактически позволяющие использовать элементы суперкомпиляции в C++. Если какие-либо значения известны на этапе компиляции, то все, что зависит исключительно от них, можно посчитать еще до исполнения программы.
- Улучшения и изменения шаблонов, лямбд, добавление новых атрибутов и прочие небольшие, но удобные изменения в языке.
- Изменения и добавки в стандартную библиотеку: поддержка работы с диапазонами (а-ля range в Python), изменения в контейнерах, работа с календарем.
- Корутины (сопроцедуры). Долгожданная возможность, позволяющая писать асинхронные программы без использования потоков. Экономит системные ресурсы, естественным образом использует асинхронные возможности ввода-вывода.
- Прочие возможности параллельного программирования, связанные с использованием потоков и механизмов синхронизации, с моделями памяти.
Во всех главах автор приводит подробные и объемные примеры программного кода. Из-за важности параллельного и асинхронного программирования в книге есть отдельная глава «Практические примеры». И хотя примеры в ней носят учебный, а не производственный характер, они показывают использование C++ в уже вполне «взрослых» ситуациях.
В конце можно ознакомиться с тем, что, по мнению автора, ждет нас в C++23. На момент публикации ожидалось следующее:
- новые возможности стандартной библиотеки,
- поддержка контрактов,
- описание пред-, постусловий и инвариантов для более жесткого контроля исполнения программ и сопоставления с образцом (оператор inspect, более мощный, чем if и switch, по возможностям аналогичный match в функциональных языках).
Следует отметить, что несмотря на то, что некоторые новые возможности языка описаны в стандарте, они все еще не реализованы в полной мере компиляторами и стандартными библиотеками. В особенности это касается модулей — наиболее значимого на мой взгляд изменения. Их цель — оторваться от препроцессора и унаследованной от C и языка ассемблера модели раздельной компиляции и реорганизовать трансляцию больших программных проектов так, как это сделано в более современных языках. Актуальные компиляторы не могут полностью реализовать стандарт С++: в той или иной мере это несоответствие наблюдается на протяжении последних 25 лет.
Что хорошего в книге
Позволяет сформировать первое впечатление о манере изложения и о том, что можно почерпнуть из книги, а что нет.
В оригинале — Distilled Information, то есть «дистиллят» изученного, то, что можно выделить в сухом остатке. Это краткие тезисы, которые помогают «собрать мозги в кучу» и четко зафиксировать то, что мы только что узнали. Вот пример:
Переводы технической литературы иногда бывают такими, что хватаешься за голову, бежишь читать оригинал и не возвращаешься. Но в данном случае и он, и научная редактура выполнены твердой университетской рукой — решения выглядят взвешенными, на них можно положиться.
По оформлению русскоязычная версия местами превосходит оригинал, в значительной степени основанный Райнером Гриммом на подборке материалов из собственного блога. Вот пример:
Казалось бы, чепуха. Но, когда читаешь нетривиальный пример кода с пояснениями, видишь скриншот консоли с результатами работы программы (иногда неожиданными!), а текст прочитать не можешь, разве это пустяк?
Что бы сделало книгу лучше
Большинство иллюстраций появляются в начале разделов и изображают Сиппи в каком-нибудь забавном виде. В оригинале — Cippi, маскот книги, нарисованный супругой Райнера Гримма. Имя персонажа созвучно с «CPP».
Редактор русского перевода в предисловии написал, что подписи к рисункам катастрофически неинформативны: «Было желание убрать все эти бессмысленные рисунки из книги, но, в конце концов, было решено смириться с этим».
С информативностью у Сиппи действительно беда. Картинки выше предваряют главы про новые возможности C++ 20: модули (левая иллюстрация), корутины (правая). Пользы никакой, а правая иллюстрация так вообще скорее дезориентирует. С другой стороны, книги по C++ — в принципе не самое легкое чтиво, а небольшая добавка бессмысленного абсурда слегка разгружает читателя. Так что хорошо, что Сиппи пережила перевод и научное редактирование и никуда не делась.
Некоторые иллюстрации более-менее информативны, но «плотность» информации в них все же вызывает сомнения. Вот пара примеров. Стоило ли ради этого рисовать отдельные картинки?
Бросается в глаза, что у иллюстраций нет единого стиля, но это обусловлено тем, что материалы англоязычной версии автор взял из личного блога. В интернете разобщенные картинки выглядят привычно и уместно, но в формате книги — уже нет. Издание бы только выиграло, если бы схемы перерисовали и перевели на русский язык. Периодически попадаются и информативные примеры:
Но она не авторская и вообще относится не к C++, а к Haskell.
Качественные иллюстрации для сложного текста очень важны — они помогают его лучше понять. Надеюсь, в будущих редакциях книги или перевода Сиппи и сама получше разберется в C++, и поможет сделать это читателю. Например, покажет не только забавные картинки из жизни персонажей детских книжек, но и доходчивые диаграммы, поясняющие тяжелый материал.
Нереализованная возможность содержательно проиллюстрировать книгу — пожалуй, самый существенный недостаток. И в переводе он не был исправлен.
Незначительный недостаток — с кодом в книге неудобно работать. Что с ним можно сделать? Перепечатать вручную, сохранить в файлах, откомпилировать… Но ведь у автора есть репозиторий на GitHub с примерами из книги. Однако на него не ссылаются ни в оригинале, ни в переводе. Почему его прячут — большая загадка.
Пока приходит в голову лишь одна слегка безумная идея. И автор, и переводчики надеются на то, что читатель вручную наберет какие-то фрагменты кода и в итоге лучше усвоит материал.
Кому книга будет полезна
«С++ 20 в деталях» ориентирована в первую очередь на опытных программистов и архитекторов — тех, кто уже знаком с C++, практикует его использование и знает практическую цену описанным нововведениям. Да, автор рассказывает, зачем нужно конкретное обновление, где от него станет лучше. Но для полноценного понимания необходимо больше контекста из практического опыта. Искушенный читатель не раз подумает: «Да, это как раз то, чего мне не хватало!»
Тем, у кого опыта программирования на C++ нет или почти нет, я бы рекомендовал присмотреться к другим книгам и активностям. Это могут быть:
- Любые книги по программированию на C, если это тоже в новинку. Хотя современный C++ зачастую не поощряет использование конструкций и приемов C, иметь о них полноценное представление все же необходимо. Точно не ошибется тот, кто выберет «Язык программирования Си» Б. Кернигана и Д. Ритчи.
- онлайн-курс на Stepik от CS-центра (базовый курс и его продолжение)
- курс МФТИ
- книга Скотта Мейерса «Эффективный и современный C++»
И, конечно, лучшим дополнением теоретических знаний станет практический опыт программирования на C++.
Райнер Гримм называет свою книгу «справочным руководством». И, на мой взгляд, зря: для таких целей лучше заглянуть на C++ Reference. Для справочной литературы материал слишком «плавный»: его комфортно читать от корки до корки, проникаться идеями. Искать же в ней какие-то детали — занятие небыстрое.
Что ещё изучить по теме
Книга мне понравилась, но ограничиваться ею точно не стоит. Сложные для понимания вещи часто требуют изучения нескольких источников. Разные авторы обычно с разных сторон смотрят на одни и те же вопросы. Прочитал две статьи вместо одной — получил «стереоизображение» вместо плоской картинки.
Какими источниками стоит дополнить книгу?
Про корутины, когда они только появлялись в C++, IT-специалисты поговаривали, что «авторы стандарта превзошли себя, теперь уж точно никто ничего не поймет». Так что по этой теме лишней информации не бывает. Добавим следующие источники:
- David Mazières. My tutorial and take on C++20 coroutines
- C++20. Coroutines
В более сжатом, но толковом виде с нововведениями C++ 20 можно познакомиться на Хабре в цикле статей Яндекс.Практикума:
Когда приобретаешь новые знания о не самом простом языке программирования, первым тебя обычно оценивает транслятор. Чтобы избежать сюрпризов, стоит читать документацию конкретных компиляторов и прочих инструментов. Например, документацию CLang по модулям. Здесь мы видим, что новые возможности реализованы не полностью. Кстати, автор книги больше всего хвалит компилятор компании Microsoft, поэтому описанием его реализации модулей точно пренебрегать не стоит.
Добавлю в тему корутин и модулей. Здесь следует изучить примеры, подтверждающие, что это уже не «вещи в себе». То есть в C++ не просто решили «сделать, как у других, и положить на полку», а добавили действительно востребованную функциональность. Свидетельством актуальности и готовности к применению корутин в работе могут выступить:
- библиотека IOneRing, предоставляющая интерфейс с использованием корутин C++ к подсистеме асинхронного ввода-вывода Linux io_uring,
- отдельный небольшой проект, по которому мой коллега из YADRO Илья Казаков сделал тематический доклад на конференции C++ Russia.
Модули тоже не остаются без внимания. Их поддержка находится в экспериментальном состоянии в популярной системе сборки CMake. Заметьте, ответ на Stackoverflow появился в 2020 году, но дополняется до сих пор.
Наконец, стоит изучать основные возможности и следить за изменениями других языков программирования: Java, C#, Python, Go, Rust. Сравнивайте их с С++, изучайте реализацию — так вы лучше поймете этот язык. Этот совет, конечно же, универсальный и работает во все стороны: кто решит освоить Rust — не ленитесь оглядываться и на «плюсы».