Интервью с Бьерном Страуструпом о языке C++

в 15:47, , рубрики: c++, c++11, интервью, переводы, Страуструп, метки: , , , ,

image
Считанные часы остались до Нового 2014-го года, в котором в числе прочего всем нам был обещан новый стандарт C++14. Однако он будет не большим самостоятельным обновлением, а лишь доработкой C++11, багфиксом, который придаст текущей версии языка завершенный вид. На этом фоне Уильям Вонг (англ. William Wong) от ресурса electronicdesign.com взял интервью у Бьерна Страуструпа (дат. Bjarne Stroustrup), создателя C++. Беседа затронула несколько тем: от истории разработки C++ и особенностей стандарта C++11 до проблемы обучения этому языку программирования.

Некоторые термины и понятия мне раньше встречались исключительно в английском варианте (например, словечко embedded в контексте IT), и мне не всегда удавалось найти общепринятый перевод, в котором я не был бы уверен сам. В таких и других неоднозначных случаях я указывал английский вариант термина в скобках либо вовсе оставлял его непереведенным.

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

Translated from an article produced by Electronic Design, October 29, 2013, electronicdesign.com/dev-tools/interview-bjarne-stroustrup-discusses-c


На сегодняшний день языки программирования C и C++ являются самыми востребованными в области встраиваемых (embedded) систем, а также в задачах построения платформ для сторонних приложений. Автором C++ является Бьерн Страуструп. Он и поныне принимает активное участие в разработке стандартов этого языка программирования, в том числе последнего — C++11. Многое об этом языке он писал в своих книгах, например, в “Programming: Principles and Practice using C++” . Бьерн Страуструп любезно согласился ответить на несколько вопросов о самом языке C++ и о его разработке.

Как вы пришли к разработке C++?

Я работал над проектом, который позволил бы разделить ядро Unix на несколько частей, исполняемых мультипроцессором или высокопроизводительной локальной сетью. И мне потребовался инструмент, который позволил бы работать с аппаратной частью, обеспечивал бы хорошую производительность для задач системного программирования, а также мог бы использоваться для работы над системой со сложной архитектурой. Однако на тот момент (1979-1980 гг.) ни один из существующих языков программирования не удовлетворял всем трем условиям сразу. Поэтому я решил добавить к C концепцию классов — наподобие той, что была в Simula. Вначале я реализовал проверки и преобразования аргументов функций (впоследствии они стали прототипами функций), конструкторы, деструкторы, а также простейшее наследование. Первая версия языка C++ называлась «C with Classes». Кстати, любопытно, что для простейшей поддержки обобщенного программирования (generic programming) в ней использовались макросы. В дальнейшем я понял, что такой подход не обеспечивает должной масштабируемости, и вместо макросов добавил шаблоны.

Я совершенствовал архитектуру и реализацию языка C++ следующие несколько лет, вплоть до его коммерческого релиза в 1985 году. В то время производительность и скорость обращения к аппаратной части были очень важными характеристиками, впрочем, как и сегодня. Я счел необходимым реализовать в C++ все возможности языка C, причем сделать их не менее эффективными. Например, на ранних этапах я обнаружил, что структуры, используемые для реализации конструктора копирования, занимали на 3% больше памяти, чем в C. Я решил, что так быть не должно, и к концу недели все исправил. Чтобы программистам не пришлось отказываться от классов без какой-либо потери процессорного времени, были также добавлены встраиваемые (inline) функции. Я вообще был уверен в том, что используемые средства должны быть не только выразительными, но и достаточно эффективными, чтобы их можно было использовать в приложениях с самыми высокими требованиями.

К чему вы стремились при разработке языка C++?

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

Это требование влечет за собой использование строгой статической типизации, в отличие от слабой типизации в C.

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

О ключевых принципах, которых я придерживался при разработке C++, я рассказал в своей книге«The Design and Implementation of C++» и в двух статьях, написанных для конференции «History Of Programming Languages». Если вкратце, то я ставил такие цели:

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

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

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

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

Сложно сказать. Разработка формального стандарта — это очень непростое и, как правило, муторное дело. Им занимаются люди с большим опытом, однако все они — специалисты по совершенно разным областям программирования, и каждый имеет свое видение стандарта. Так что прийти к единому мнению может быть сложно и накладно по времени, но это необходимо: удовлетворить всем требованиям сразу не получится, а принуждать программистов пользоваться принципиально новыми инструментами нельзя. Прогресс происходит только когда в стандарт включаются дополнения, важность которых общепризнана. Нельзя принимать участие в комитете и при этом постоянно зацикливаться на мелочах. Нужно уметь видеть всю картину в целом и приходить к общему мнению с остальными. По моим подсчетам, в комитет входит около сотни организаций и, может, больше трехсот собственно разработчиков. Это в два-три раза больше, чем было раньше. Только на последних собраниях присутствовало около ста человек.

В 2014 году мы планируем выпустить новый стандарт, C++14. В нем будут минимальные нововведения, а также несколько исправлений, за их необходимость уже проголосовало большинство комитета. Я рассчитываю, что в 2014 году все уже будут использовать C++14, а после этого мы планируем выпустить C++17 в 2017 году. Но это обновление будет уже намного существенней, так что тут сложно судить о сроках.

Комитет по разработке стандартов ISO C++ сам по себе не располагает какими-либо ресурсами, будь то деньги или штатные разработчики. Он полностью основан на средствах его участников. Например, чтобы входить в состав комитета, они платят 1200 долларов ежегодно. Всякий может заявить, что, мол, в C++ нет нормальной библиотеки для создания графических интерфейсов или нормальной поддержки параллелизма задач. Да, мы в курсе. Чем жаловаться, лучше бы помогли довести эти задачи до ума. У нас очень мало прикладных программистов, и часто получается так, что нововведения создаются в угоду интересов одного конкретного разработчика.

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

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

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

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

Вот серьезно, скажите: неужели вектора из стандартной библиотеки сложнее массивов из C? Или, например, почему студентов приучают к функции qsort(), хотя sort() и эффективнее, и универсальнее? У C++ более строгая типизация, чем у C, за счет этого объектный код обрабатывается быстрее.

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

Мне тоже не нравится C++ таким, каким его представляют авторы тех учебников. В ответ я написал свою книгу для студентов и самоучек — «Programming: Principles and Practice using C++». Для ее изучения опыт программирования не обязателен, однако она вызвала интерес и среди опытных разработчиков.

Только если вам нужен просто обзор C++11, то эта книга будет довольно большой. Для этой цели я порекомендовал бы книгу «A Tour of C++». В ней описаны все ключевые моменты ISO C++ и стандартной библиотеки всего на 180 страницах. Стандарт C++11 полностью поддерживается компиляторами Clang и GCC, частично — Microsoft C++ и многими другими, правда, боюсь, на менее популярных платформах он может выполняться некорректно.

В C++ 11 было много нововведений, в том числе лямбда-выражения и поддержка многопоточного программирования. Как вы считаете, оказались ли они востребованными?

Для работы с потоками мне постоянно приходилось пользоваться сторонними библиотеками. Они были хороши, но последние пятнадцать лет я хотел добавить поддержку потоков именно в стандарт, чего мы наконец и достигли. С точки зрения параллельного программирования ключевые новшества C++11 состоят в организации памяти (которую, к слову, заимствовали и для языка C), а также портируемости многопоточных программ. Однако если вы программируете на уровне потоков и барьеров, то самым главным для вас может оказаться безопасное преобразование типов (type safety): для разделения и передачи данных между потоками больше не требуется никаких макросов или void**. Впрочем, для кого-то также важны инструменты и для lock-free программирования.

Что касается лямбда-выражений, то я примерно лет десять работал над такой их реализацией в языке C++, от которой было бы больше пользы, чем вреда. У сторонних библиотек как правило страдает производительность. Нам же удалось добиться для лямбда-выражений производительности, сравнимой с циклом for. Приведу пример:

double sum = 0;
for (int i=0; i<v.size(); ++i) sum += v[i];    // array style
double sum = 0;
for (auto p = v.begin(); p!=v.end(); ++p) sum += *p; // pointer style
double sum = 0;
for_each(v.begin(),v.end(), [&](double d) { sum += d; });   // algorithm style

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

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

sort(v, [](const string& a, const string& b) { return a>b; }); // sort in reverse order

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

Также лямбда-выражения открывают большой простор для практики написания кода. Здесь, наверное, не место учить вас C++11, но позвольте мне привести один пример:

template<typename C, typename V>
vector<Value_type<C>*> find_all(C& cont, V v)  // find all occurrences of v in cont
{
    vector<Value_type<C>*> res;

    for (auto& x : cont)
        if (x==v)
            res.push_back(&x);
    return res;
}

В этом коде я применил несколько новых для C++ вещей. Цикл for, например, здесь читается как «для всех x из cont» и упрощает перебор контейнера cont. Объявление auto& x показывает, что x должна быть ссылкой на тип элементов инициализирующего контейнера, в данном случае — на тип элементов cont. Данный цикл собирает адреса всех вхождений v в cont и складывает их в вектор указателей res. Так что эти конструкции — не больше чем синтаксический сахар, хотя они весьма удобны.


Существенное же нововведение заключено в return: обратите внимание, что я по вернул вектор по значению. В C++98 этот оператор возврата создал бы копию res, а ведь на деле он может оказаться большим и состоять из тысяч элементов. С точки зрения производительности это было бы весьма опрометчиво. А в C++11 у векторов есть так называемый конструктор перемещения (move constructor), который вместо копирования «заимствует» представление res (в сущности, всего три указателя) для использования на месте вызова функции find_all(), а сам вектор оставляет пустым. После выполнения return мы больше никогда не сможем использовать res. Таким образом, возврат вектора по значению обойдется максимум в шесть присваиваний, вне зависимости от размера вектора.

Конструкторы перемещения — довольно простой инструмент. Он доступен каждому программисту, и, кроме того, реализован в каждом контейнерном классе стандартной библиотеки. Это позволяет без труда возвращать из функций большие объекты и не ломать лишний раз голову над управлением памятью.

Протестировать функцию find_all() можно следующим образом:

void test()
{
string m {"Mary had a little lamb"};

for (const auto p : find_all(m,'a'))            // p is a char*
    if (*p!='a')
        cerr << "string bug!n";

vector<string> v {"Mary”, “lamb", “Mary”, “mary”, “wolf”};

for (const auto p : find_all(v,”Mary”))    // p is a string*
    if (*p!=”Mary”)
        cerr << "vector<string> bug!n";
}

Попробуйте написать тот же код без применения шаблонов и нововведений C++11 и сравните результаты.


На эту тему рекомендую прочесть «A Tour of C++», за деталями же обратитесь к четвертому изданию книги «The C++ Programming Language».

Какие часто встречаемые у современных C++-программистов ошибки вы можете отметить?

Они почему-то часто думают, что должны выбирать между эффективным и изящным кодом. Они либо ограничивают себя низкоуровневыми возможностями языка (ради «эффективности»), либо выстраивают огромную архитектуру «на все случаи жизни» (полагая такой код весьма элегантным). На мой же взгляд, идеал состоит в сочетании максимальной эффективности и наиболее оптимальной архитектуры. Так происходит, когда решение программиста самым оптимальным образом удовлетворяет условиям задачи, но это, разумеется, не всегда возможно. Редко получается добиться этого с первой попытки, но так случается довольно часто, насколько это возможно для идеала.

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

Чем вы любите заниматься в свободное время?

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

Примечания переводчика
  1. «Программирование. Принципы и практика использования C++» в переводе Дмитрия Клюшина, издательство «Вильямс». назад
  2. О типизации см. статью Ликбез по типизации в языках программирования. назад
  3. Вероятно, здесь все-таки имеется в виду книга «The Design and Evolution of C++» — «Дизайн и эволюция C++» в переводе издательства «Питер». назад
  4. Модель памяти C++11 подробно рассмотрена в статье Lock-free структуры данных. Основы: Модель памяти. назад
  5. Краткий обзор move semantics в C++11 приведен в статье Move semantics в C++11 и STL-контейнеры. назад
  6. В целом ключевые нововвдения C++11 (в том числе и вышеупомянутые) освещены в статье Десять возможностей C++11, которые должен использовать каждый C++ разработчик.
  7. В русском переводе Николая Мартынова, издательство «Бином», книга называется «Язык программирования C++», однако я смог отыскать только перевыпуск бородатого, неактуального издания 2001-го года. Других переводов не нашел вовсе. назад

Автор: kazarey

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js