Мощный, гибкий, сложный: история C++ началась еще 40 лет назад, и по сей день он остается одним из наиболее широко используемых языков программирования. TechRepublic в беседе с создателем C++ Бьерном Страуструпом попытался выяснить, в чем его особенность.

Бьерн Страуструп. Изображение: National Academy of Engineering
Бьерн Страуструп. Изображение: National Academy of Engineering

Бьерн Страуструп: «Эволюция необходима для того, чтобы справляться с вызовами меняющегося мира и реализовывать новые идеи». ("Evolution is necessary to meet the challenges of a changing world and to incorporate new ideas.")

История C++ начинается в 1979 году, когда Бьерн Страуструп, создатель этого языка программирования, впервые начал работу над языком, который тогда был известен как «С c классами» (C with Classes). Изначально язык разрабатывался, как новая улучшенная версия языка программирования C с добавлением дополнительных фич, которые сделали его объектно-ориентированным.

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

«Моей целью было создать мультикомпьютерную платформу с коммуникационной системой, в качестве которой могли бы выступать либо общая память, либо сеть», — объясняет Страуструп.

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

Ни один язык, из существующих в то время, не подходил для выполнения этих задач, и Страуструп взял на себя миссию создать такой язык. «“C c классами” объединил в себе способность языка C работать “близко к железу”, с эффективной реализацией классов Simula для абстракции и организации кода», — говорит Страуструп .

С самого начала Страуструп понимал, что у него нет единовластного контроля над языком. На ранних этапах над C++ работали только он сам и несколько его коллег из Bell Labs, а когда начались попытки стандартизировать язык, число вовлеченных человек возросло до нескольких десятков.

На сегодняшний день в комитете по стандартам языка программирования C++ насчитывается около 400 членов, не считая гораздо более широкого сообщества пользователей, которые следят и стараются влиять на направление развития языка.

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

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

Вспомните «Вазу»

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

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

«Для параллелизма была важна поддержка типобезопасности. C++11 предоставил плотно скомпанованную сеть взаимно поддерживающих фич, таких как [constexpr] функции для вычислений во время компиляции, лямбда-выражения, автоматический вывод типов и вариативные шаблоны».

В то время как C++11 представил несколько новых фич и возможностей, которые сделали язык проще, быстрее и выразительнее, язык по прежнему сохраняет репутацию трудно осваиваемого. И его создатель полностью с этим согласен.

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

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

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

Тем не менее не все хорошие фичи можно внедрить. «Вашей задачей является добавление только тех нескольких фич, которые действительно необходимы людям — ведь мы не можем добавлять каждую фичу, которая кому-то в чем-то может помочь, потому что при таком сценарии язык не выдержит собственной тяжести», — объясняет он.

Страуструп привел в пример «Вазу», величественный шведский линкор XVII века, который затонул из-за плохой конструкции в гавани Стокгольма во время своего первого рейса. 

«По настоянию Короля — то бишь высшего руководства — и вопреки здравому мнению технических специалистов корабль был завален красивыми статуями и огромными пушками. В итоге из-за смещенного центра тяжести линкор был опрокинут порывом ветра».

«Я неоднократно привожу в пример “Вазу”, как своего рода предостережение для людей, которые с большим рвением хотят улучшить C++ посредством добавления новых фич: помните, что случилось с “Вазой”! C++ до сих пор на плаву».

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

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

«Можно найти простые программы, написанные в первые годы разработок — примерно 40 лет назад — которые все еще работали бы сегодня», — говорит Страуструп.

«Стабильность — важный критерий для языка, используемого для систем, которые должны работать десятилетиями. В действительности, многие из первоначальных идей получилось реализовать только в C++20. Я с самого начала знал, что не смогу построить идеальный язык, поэтому должен был настраиваться на постепенное развитие — эволюцию. На самом деле, я не верил в идею создания идеального языка — идеального для чего? Для кого?».

Страуструп добавляет: «Эволюция необходима для того, чтобы справляться с вызовами меняющегося мира и реализовывать новые идеи».

C++ на практике

На сентябрь 2020 года C++ является четвертым по популярности языком программирования в мире после C, Java и Python, и — согласно последнему индексу TIOBE - является самым быстрорастущим. C++ — это язык программирования широкого применения, пользующийся успехом у разработчиков за его мощность и гибкость, что делает его идеальным для операционных систем, веб-браузеров, поисковых систем (включая Google), игр, бизнес-приложений и многого другого.

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

Также Страуструп отметил, что, несмотря на его широкую популярность, трудно со всей точностью определить, где и для чего используется C++.

«По предварительной оценке можно сказать, что “везде”», — говорит он.

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

Страуструп также подчеркивает относительную ненадежность опросов разработчиков при оценке популярности того или иного языка программирования: «Собрать мнения программистов — и сложная и в тоже время простая задача. Интернет-опросы обычно являются просто индикатором “шума”; то есть отражением того, о чем говорят, а не то, что по факту используется».

Будущее C++

Сегодня Страуструп является техническим партнером  (Technical Fellow) в Morgan Stanley. Его работа с Международной организацией по стандартизации (ISO) над стандартом C++ и C++ Core Guidelines считаются частью его обязанностей в рамках сотрудничества с финансовым гигантом, и он по-прежнему также активно участвует в разработке C++. 

В частности, Страуструп входит в координационную группу, которая презентует и обсуждает рекомендации о будущем языка программирования. Он также курирует группу по эволюции языка и принимает участие в обсуждениях новых фич. 

Однако, что касается повседневной работы над C++, Страуструп с радостью берет на себя более второстепенную роль. «Я занимаюсь административной деятельностью, но стараюсь делать это как можно меньше. Я не самый лучший администратор», — признает он.

До пандемии Страуструп много путешествовал, преподавая и объясняя язык C++ всему миру в своих книгах, статьях и интервью — как и для всего в остальном мире, 2020 временно приостановил эту деятельность.

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

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

Пандемия COVID-19 также сильно препятствовала работе над следующими двумя версиями языка C++20 и C++23, однако Страуструп утверждает, что “почти весь” C++20 будет готов к 2020 году.

«Кроме того, мы работаем над Unicode, численными операциями, разработкой игр, понижением задержки, инструментами, AI и многим другим», — говорит он.

«Мы выпускаем фичи (библиотеки и язык) по готовности, а обновленный стандарт выходит каждые три года. C++14, C++17 и C++20 были выпущены без задержек». Стоит отметить, что усилия по разработке стандартов и основные разработчики все на одной волне.

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

Спасибо за внимание!


Материал подготовлен в рамках курса «C++ Developer. Professional».

Всех желающих приглашаем на бесплатный двухдневный интенсив «Асинхронный сервер на сопрограммах из C++20».

В данном интенсиве мы рассмотрим как можно сделать обертку над асинхронными сокетами под Linux, которую можно будет использовать для передачи управления с помощью сопрограмм. Итоговый результат будет интересно сравнить с классическим решением на основе функций обратного вызова, чтобы проверить насколько для сопрограммы выполняется принцип zero-overhead abstractions.
>> РЕГИСТРАЦИЯ

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


  1. SirEdvin
    26.11.2021 19:19
    +12

    Статья звучит так, как будто именно С++ используется везде, хотя очень часто это все еще просто С


  1. Sadler
    27.11.2021 12:54
    +3

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


    1. allcreater
      28.11.2021 15:50
      +1

      Да, вы совершенно правы, несмотря на то, что за очень редким исключением язык де-факто обратно совместим с C (про де-юре это сказать трудно, некоторый сишный код приводит к undefined behavior), у сообщества C++ сформировался специфический набор идиом и подходов, характерный только для этого языка. Так что хороший сишник вряд ли быстро станет хорошим плюсовиком, так же как и хороший плюсовик быстро не станет хорошим сишником.

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


    1. Reformat
      29.11.2021 11:31
      +1

      Для себя вывел такой подход к C++ чтобы упростить жизнь и не задумываться о нюансах 20 способов инициализации и 10 способах передачи аргументов:

      • Используем современный функциональный C++

      • Автоматический вывод типов везде где это возможно

      • Чистые функции с возвращаемым значением

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

      • Организация кода по принципу один файл - одна одноименная сущность в нем (class, namespace, function).

      • По умолчанию во всех параметрах используем специальные невладеющие типы-значения (например string_view x), если их недостает, то T const & (например string const &x).

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


      1. Sadler
        29.11.2021 11:42
        +1

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


        1. Reformat
          29.11.2021 12:02
          +1

          Дело в том, что в C++ долгое время был принят "эффективный подход", когда вместо возврата большого обьекта его записывали по мутабельной ссылке, например:

          // Старый стиль C++
          
          void collectApples(vector<Apple> &to) { ... }

          Хотя уже лет пятнадцать как можно писать

          // Современный стиль C++
          
          vector<Apple> collectApples() { ... }

          То есть, можно возвращать большие объекты как значения, даже без std::move, компилятор всегда оптимизирует это, и никакого копирования не происходит.

          Но привычки сильны и целая куча людей до сих пор пилит как в 90-ых научились.

          Поэтому и подчеркнул важность функционального подхода, препятствием для него является именно стереотип о неэффективности.


          1. allcreater
            29.11.2021 12:46
            +1

            Подождите, а только ли привычки?

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

            Попробовал даже забенчмаркать, результат с возвратом по значению довольно в два раза дольше :/

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


            1. Reformat
              29.11.2021 13:14

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


              1. allcreater
                29.11.2021 14:08

                Нет, такого быть, конечно, не может — тут явно сработала оптимизация, компилятор ведь видит, что переменная не используется. Добавление в обоих случаях benchmark::DoNotOptimize возвращает пример на круги своя


                1. Reformat
                  29.11.2021 18:03

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


                1. Izaron
                  30.11.2021 22:01
                  +2

                  Извините, но вы оба считаете что-то не то...

                  Это первый вариант (оптимизированный):

                  std::vector<int> result(NumOfValues);
                  for (много раз) {
                  	result.resize(NumOfValues);
                  	std::iota(out.begin(), out.end(), 1);
                  }

                  Это второй вариант (оптимизированный):

                  for (много раз) {
                  	std::vector<int> out(NumOfValues);
                  	std::iota(out.begin(), out.end(), 1);
                  }

                  Вызов resize не влияет совсем, так как размер вектора уже достаточный. Но во втором коде лишняя аллокация и деаллокация памяти, из-за чего второй вариант "типа" медленнее. Я поменял одну строку, результат эквивалентный: https://quick-bench.com/q/fSU3z97n18YhNGBgHmojrtW0Y0U


                  1. allcreater
                    30.11.2021 22:08

                    В своём бенчмарке я хотел показать, что передача по ссылке более эффективна, чем RVO, при условии, что где-то уже существует заранее выделенный контейнер (например, это статический thread_local объект).

                    Если же объект создаётся всегда новый - никакой разницы, конечно же, не будет.


                  1. allcreater
                    30.11.2021 22:36

                    упс, прошу прощения, в первый раз поторопился с ответом, не вглядевшись в код бенчмарка. Подправил ответ. Надеюсь, теперь получилось по существу >_<


    1. Alex_ME
      29.11.2021 12:46

      Вот возьмем мы "Си с классами", и окажется, что там без темплейтов не выразимы такие полезные вещи, как коллекции/алгоритмы. Что, опять void* гонять, как в Си?

      Потом выясняем, что неплохо было бы как-то упростить работу с памятью (умные указатели), а что-то позволяет сильно оптимизировать (move семантика), а можно и синтаксического сахара добавить... И пошло-поехало.

      Я сишник, мне C++ сложен своими деталями и нюансами (здесь так, это сяк, а вот тут UB), но верхнеуровнево фичи языка я нахожу полезными.


      1. Sadler
        29.11.2021 13:57
        -1

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

        X x;
        auto a = std::async(&X::foo, &x, 42, "Hello");
        a.wait();

        Во многих других высокоуровневых языках такая же конструкция будет записана в виде:

        await x.foo(42, "Hello")

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


        1. allcreater
          29.11.2021 14:14
          -1

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

          К сожалению, стандартных типов для корутин ввести(как и добавить их поддержку в std::future) ещё не успели, если повезёт, появятся в С++23, если нет — придётся городить свои или пользоваться библиотекой Folly или чем-то подобным.


          1. Sadler
            29.11.2021 14:20
            +1

            Спасибо, интересно. Быстро просмотрел картиночки в статье, весьма обнадёживает, буду следить за развитием корутин в плюсах.


        1. Tuxman
          29.11.2021 23:13

          IMHO, тут принципиальная разница.

          a.wait()

          Заблокирует текущий тред выполнения, ожидая результата.

          await x.foo()

          Вернёт выполнение, это же корутина, и текущий тред сможет переключится на выполнение другой задачи в очереди, реализуя тем самым "green thread".


          1. Sadler
            29.11.2021 23:22

            Сильно зависит от реализации. Для javascript, например, не принципиально.


  1. aasten
    27.11.2021 14:23

    Интересно, почему статья ушла в минус


    1. alliumnsk
      27.11.2021 15:25
      +4

      Потому что пора переходить на Хаскель (-:
      /s


      1. loltrol
        27.11.2021 16:26
        -2

        на ржавого


        1. alliumnsk
          28.11.2021 10:35
          +2

          Полумеры!


          1. Tuxman
            29.11.2021 23:17

            Кстати, почему Golang c 13 на 18 место упал в рейтинге?https://www.tiobe.com/tiobe-index/

            Все побежали программировать на Python? Нанометры рулят, кого волнует рантайм перформанс?


    1. Zada
      27.11.2021 17:22

      Мое мнение, потому что статья вообще не понятно о чем. Что-то кто-то сказал. По 3 раза повторы. Вода. Реферат.


      1. da0c
        28.11.2021 13:45
        +2

        Единственное, что нового узнал - что Страуструп партнёр в Морган Стенли.


        1. Tuxman
          29.11.2021 23:19

          Научат ругаться матом, там много русских, у меня был офер от Нью Йоркского Морган Стенли, собеседовали все русские.


    1. bromzh
      27.11.2021 23:26
      +1

      Потому что цель статьи по большей части реклама своих курсов


  1. N0zzy
    28.11.2021 00:29
    +2

    Указано, что будет описание про плюсы и их будущее, а ушли в описание как живёт и чем дышит сейчас господин СтраусТруп.


  1. arm039
    29.11.2021 01:17

    C++ вокруг больше чем нужно, но в основном в прикладном программировании.


  1. demp
    29.11.2021 18:39

    И в чем смысл переводить статью годовалой давности? Так то C++20 уже принят и полным ходом разработка C++23


    1. Tuxman
      29.11.2021 23:21

      И c каждым новым стандартом всё надеяться, что std::future станет пригодной для практического использования, чтобы корутины заработали "из-коробки", и без Фейсбучных Folly. К слову, std::future появился ещё в C++11, но был мало пригоден.