Карло Пешио
pescio@acm.org

Судя по откликам наших читателей, статьи классиков computer science и беседы с ними всегда вызывают большой интерес. Сегодня мы представляем интервью с Бьерном Страуструпом - одним из основоположников объектно-ориентированной парадигмы программирования и автором C++. (Кстати, сейчас в Internet опубликован некий материал, представляющий будто бы данное Страуструпом чрезвычайно острое и самокритичное интервью журналу IEEE Computer. И сам Страуструп и редакция журнала охарактеризовали эту публикацию как скандальную мистификацию. Computer на своем Web-узле пообещал выступить с разъяснениями и в ближайшем номере напечатать настоящее интервью.)

?: Удовлетворены ли Вы ходом и результатами процесса стандартизации C++? Здесь ведь не все однозначно: с одной стороны - это очень веское доказательство успеха созданного Вами языка; с другой - отныне установлены жесткие "бюрократические" рамки, которые неизбежно ограничат и авторскую свободу его возможного дальнейшего совершенствования.

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

Я принимал участие в деятельности по стандартизации, поскольку комитет был созван еще до завершения мною уже намеченной работы над языком. Это послужило хорошим стимулом. С++ без шаблонов (templates) и исключений (exceptions) не мог считаться полностью дееспособным; без поддержки пространств имен (namespaces) и информации о типах во время исполнения (run-time type information - RTTI) язык также был бы намного беднее, чем мы имеем сегодня. Что же касается той "первоначальной авторской свободы", то я не стал бы ее преувеличивать. Я с самого начала четко определил для себя два очень существенных ограничения: я считал необходимым обеспечить совместимость моего языка с языком Си и я хотел дать пользователям инструмент для решения реальных задач. Речь не шла о проектировании еще одного "культового" языка.

Сообщество С++ безусловно нуждалось в хорошо определенном стандарте, и я думаю, усилия, вложенные в процесс стандартизации за последние шесть лет, не пропали даром. Я был председателем подкомитета, рассмотревшего все предложения по расширению языка и внесению значимых изменений, а также был вовлечен и в решение большинства других вопросов - включая проектирование стандартной библиотеки. Для языка, который так широко используется, стандартизация необходима. Причем я бы не сказал, что эта работа доставляет много удовольствия. Поэтому специалисты, добровольно из года в год тратившие массу времени и усилий на работу над стандартом, заслуживают признательности со стороны всего программистского сообщества. Я постарался перечислить их имена в моей книге "Проектирование и Эволюция C++" [1].

?: Недавно вышло третье издание книги "Язык программирования С++" [2]. Хотелось бы услышать ее характеристику из уст автора. Планируется ли переиздание и другой настольной книги программистов на С++ [3]?

Новую книгу есть смысл писать, лишь когда накопилось много действительно нового материала. Стандарт ISO C++ как раз и обеспечил такую основу. Думаю, третья редакция [2] содержит полезную информацию для каждого программиста на С++, в том числе и опытного. Кстати, последние заслуживают куда большего, чем те банальности, что обычно предлагаются неофитам. В моей книге акцент сделан не столько на описании особенностей языка (хотя все они описаны весьма подробно), сколько на поддерживаемых ими методах проектирования и программирования, которые ныне достигли надлежащей степени зрелости. Что касается работы [3], то после появления стандарта, видимо, понадобится новое подобное руководство.

?: Могли бы Вы назвать такие некогда принятые Вами при проектировании С++ технические решения, которые Вы сейчас, в свете многолетнего опыта, хотели бы изменить - если бы такая возможность предоставилась (хотя понятно, что это едва ли осуществимо - скажем, по причинам совместимости)?

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

?: На самом деле я задавал свой вопрос, имея в виду прежде всего виртуальные функции. Если более конкретно, то мне, например, не слишком нравится то, что виртуальная функция может быть переопределена в "privately" производном классе, когда функция, вообще говоря, недоступна - и при этом переопределяема. Вряд ли такая особенность полезна, в том числе и с точки зрения безопасности "скрытого" (private) наследования. Вообще, о принципах наследования в С++ периодически возникают дискуссии; так, в известных статьях Марку Саккинена [5,6] утверждается, что не следует расширять обязательства по инициированию конструктора виртуального базового класса (virtual base class - VBC) далее, чем "необходимо" в графе наследования. Понимая причины, по которым правила для VBC в С++ такие, какие они есть, я все же думаю, что в определенном смысле инициирование конструктора VBC ослабляет инкапсуляцию, обеспечиваемую промежуточными классами. С другой стороны, за годы работы с языком С++ мы вполне изучили его сильные и слабые стороны, а потому реализация предложений Саккинена, кажущихся привлекательными с точки зрения надлежащего "стиля программирования", скорее всего приведет к каким-нибудь новым слабостям.

Конечно, в такого рода предложениях можно найти рациональное зерно, но я отношусь к ним как к чисто академическим, достаточно далеким от практического программирования. Да, наверное, можно спроектировать конструктор, который вызовет проблемы, но в принципе конструктор, написанный для того, чтобы инициализировать свои собственные данные, безвреден по существу. Более того, если виртуальный базовый класс является общедоступным (public) - а делать его таковым я всегда рекомендую - то предполагается, что все классы знают о его существовании и соответственно ожидают, что его конструктор будет активизирован. В этом виртуальные базовые классы не отличаются от других базовых классов. Если VBC в одном месте декларирован "private", а где-то еще как "public" и если большинство производных классов инициализируют его самому автору неведомым способом, то безусловно, "инкапсуляция, обеспечиваемая промежуточными классами", будет ослаблена. Однако, если для кого-то это главная проблема, я могу лишь позавидовать.

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

class A { 
virtual void f(); }; 
class B : private A { void f(); // implementation 
А* get_A(Rights& r) { /* check rights */ return (*A) this; } } 

?: Но с другой стороны, "скрытое" наследование не является транзитивным; поэтому, в следующем примере:

class A { virtual void f(); }; 
class B : private A { void h() { f(); } }; 
class C : public B { virtual void f(); } 

класс A не есть интерфейс для класса C; A::f() не доступен в C, но переопределяем в классе C. Конечно, можно просто сказать, что в таком не слишком органичном на взгляд построении виноват тот, кто реализовывал класс C, основываясь на деталях реализации класса B (а именно - на том факте, что класс B реализован с использованием класса A). Но, cогласитесь, можно понять тех, кто приветствовал бы исключение такого рода особенностей из языка...

Да, это довольно сомнительный пример. Однако не так-то просто предложить такое множество правил, которые делали бы нелегальным каждый сомнительный с какой-то точки зрения пример, и при этом - без ущерба для тех, кто считает допустимыми и даже существенными те конструкции, которые кому-то кажутся сомнительными. В принципе, я против ограничений, вводимых без ясно выраженной практической цели. Я не рассматривал ортогональность как основную проектную цель, но предпочитаю, чтобы она была - если нет каких-то веских причин для неортогональности. Правила доступа в С++ вполне ортогональны (вспомните правила именования, правила перегрузки и т.д.), и я не вижу никаких причин, чтобы им таковыми не быть. Находятся программисты, которых эти правила удивляют, но точно так же им не нравились бы и не столь ортогональные правила.

?: Я понимаю Ваши аргументы и во многом с Вами согласен. Именно поэтому я и задавал вопрос, имея в виду целесообразность некоторых правил не с "академической" точки зрения, а применительно к определенному стилю программирования. Бесспорно, могут быть ситуации (например, при повторном использовании библиотечных классов без доступа к их исходному коду), когда можно и нужно нарушать некоторые правила "хорошего программистского тона" - благо C++ предоставляет программисту свободу действий.

Что действительно хотелось бы усилить в C++ - так это способность вылавливать логические ошибки, сделанные программистом. По сравнению с Си, С++ обеспечивает в этом отношении больше возможностей при программировании эквивалентных действий. Однако я не думаю, что безопасность и надежность программ должны достигаться ценой усложнения языковых средств. Именно это, а также необходимость учета проблем совместимости, ставит пределы нашим стремлениям сделать язык по возможности концептуально чистым. Ну а чтобы писать на C++ более безопасные программы, рекомендую использовать стандартные библиотеки. Например, если кто-то обеспокоен тем, что у массивов не проверяется выход индекса за пределы назначенного диапазона, то он может использовать класс "вектор" с проверкой диапазона. Я именно так и поступаю, особенно при отладке.

?: Давайте вернемся к ретроспективе C++...

Плохо это или хорошо, но с самого начала работы над C++ я выдерживал высокую степень совместимости с Си и комитет по стандартизации продолжил эту линию. Это довольно сильное ограничение: многое в С++ можно было бы с технической точки зрения сделать лучше, но в свете требования совместимости с Си это было нереалистично. Более того, дефекты Си при этом, по-существу, возводились в квадрат. Я считал принципиальным построить надлежащую систему типов, и выделил именно этот комплекс проблем, когда надо было выбирать, какие узкие места Си корректировать. И пока никто не убедил меня, что имелась альтернатива моему подходу - разве что построить еще один культовый язык, который выглядел бы безупречным в глазах небольшой группы приверженцев, - но я-то ориентировался на создание практического инструмента. Скажу больше: если бы в начале работы я решил, что Си - не тот язык, с которым новый язык должен быть совместим, то я бы выбрал на его роль какой-нибудь другой язык. И я по-прежнему убежден, что нет смысла начинать с нуля и заниматься изобретением велосипеда. Зачем предлагать еще один способ записи цикла?

Если говорить о сегодняшнем состоянии С++, то актуальной проблемой остается параллельность (concurrency). Многие хотели бы видеть какую-нибудь форму параллельности, прямо поддерживаемую в С++. Проблема, однако, в том, что не существует такой формы параллельности, которая бы отвечала нуждам большинства программистов С++. Те, кто реализуют операционные системы, нуждаются в одном механизме параллельности, разработчики баз данных - в другом, программисты распределенных приложений - в третьем. Вот я и решил не включать в С++ средства, явным образом поддерживающие параллельность. Программисты, которым параллельность в том или ином виде действительно необходима, могут пользоваться соответствующими библиотечными расширениями языка. Комитет по стандартизации поддержал меня и в этом вопросе; конечно, нам известны неплохие схемы поддержки механизмов параллельной работы, но ни одна из них не удовлетворила бы всех требований - просто из-за того, что диапазон применения С++ грандиозен.

Некоторое время тому назад меня попросили написать статью о С++ для Второй конференции ACM по истории языков программирования (они проводятся раз в 15 лет) и отметить в ней то, что я считаю своей самой большой ошибкой. Так вот, есть всего лишь один кандидат на этот титул: мне не удалось написать приемлемую базовую библиотеку с тем, чтобы включить ее в версию 1.0, выпущенную в 1985 г. В свое оправдание скажу, что я в то время просто не знал, как написать действительно хорошую библиотеку и нуждался в "образцах", чтобы обеспечить эффективные, гибкие контейнеры с безопасной типизацией. Результат этой ошибки - беспорядок (мягко говоря) из-за наличия несовместимых базовых библиотек, очень различающихся как по идеологии построения, так и по качеству.

К счастью, комитет по стандартизации смог прийти к соглашению о том, как должна выглядеть действительно адекватная базовая библиотека. Так что мы теперь имеем то, что я не смог сделать (и не знал как спроектировать и реализовать) десять лет назад. Большой вклад в работу по проектированию структуры контейнеров и базовых алгоритмов внес Алекс Степанов. И надо сказать, эти годы потрачены не зря: теперь библиотечные компоненты строятся на базе отработанных за это время принципов и методов.

?: Как Вы знаете, есть предложения по добавлению некоторых присущих С++ особенностей в новую версию Си (C9x). Например, предполагается ограниченная поддержка классов. По-моему, это довольно наивный подход, например, эти классы, как предполагается, не должны иметь конструкторов, перегрузки и т.д. Какова Ваша позиция относительно этого "нового ANSI C"?

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

Попытка же взять небольшое подмножество С++ с тем, чтобы создать "более простой и почти столь же мощный язык, как С++", по моему мнению, обречена на неудачу. Главное, что необходимо еще раз подчеркнуть: С++ - это целостный организм, поддерживающий когерентные стили программирования; соответственно, его средства и особенности, спроектированные на основе реального опыта, существуют не по отдельности, а взаимообусловлены. Добавление же его подмножества, даже и к "родственному" языку, вряд ли приведет к созданию когерентного языка.

Не думаю, что я вправе подсказывать сообществу Си, как им стандартизировать язык. Да и какой в этом смысл: те, кто мог бы оценить мои советы, в большинстве своем так или иначе уже программируют именно на С++. Комитет по стандартизации Си будет действовать так, как считает нужным, и наверняка лучше меня знает нужды своего сообщества. Однако программистское сообщество не нуждается еще в одном, несовместимом с уже имеющимися, диалекте Си. К тому же надо иметь в виду эффект инерционности. "Библия Си" [4] еще не устарела; нынешний ANSI C, несомненно, останется в силе и после обнародования нового стандарта - так же, как различные марки С++ будут неплохо сосуществовать и после приобретения стандартом С++ официального статуса. Без сомнения, новые особенности C9x неизбежно станут еще одним источником нестабильности в обоих (Си и С++) сообществах.

?: Говоря о "сыновьях" С++, нельзя не упомянуть Java. Видите ли Вы реальные достоинства этого шумно рекламируемого языка? Я знаю, что Вы обычно очень осторожны в критике других языков, имея в виду, что "каждый язык имеет свою нишу". И все же, что вы скажете о Java?

Очевидно, что синтаксис Java похож на синтаксис C++. Однако это все же фундаментально иной язык, поддерживающий отличную от С++ культуру и иные (на самом деле более ограниченные) стили программирования. Java определенно не похож и на тот C++, который я - мысленно - спроектировал бы в отсутствие налагаемых совместимостью с Си ограничений. От Java ждут очень многого, особенно в связи с интеграцией с Web, и эти ожидания подстегиваются дорогостоящими маркетинговыми акциями. Время покажет, будет ли Java дееспособен как универсальный язык. Хотелось бы посмотреть, как большинство программистов и менеджеров будут реагировать на "открытие" (для многих уже сейчас очевидное), что безопасность Java и в особенности JavaScript оставляет желать много лучшего. Часто путают безопасность типов языка программирования (что корректная реализация Java обеспечивает) с собственно безопасностью - поддержкой целостности системы и обеспечением конфиденциальности и секретности, что, по-видимому, может серьезно пострадать при использовании Java. Мои коллеги в AT&T, занимающиеся проблемами безопасности, в шутку называют Java "языком реализации вирусов". Если же вернуться к сравнению С++ и Java, то вспомним следующие фундаментальные характеристики С++: абстракция данных; поддержка объектно-ориентированного программирования (ООП); "обобщенные" (generic) классы.

Изо всех этих особенностей применительно к Java в полной мере можно говорить лишь об ООП, которое, впрочем, реализуется здесь иначе, чем в С++.

?: Некоторые из новейших дополнений в С++ - подобно выполняемому в новом стиле преобразованию типов, знаменуют то, что можно назвать "бегством от Си". Как Вы полагаете, появится ли в будущем в языке больше средств, явно ориентированных на проектирование? Например, есть ли ценные в этом смысле идеи в проектах, подобных "Annotated C++" или "Larch C++", склоняющихся больше к написанию спецификаций, а меньше - к уровню кодирования? Выглядит многообещающей и идея добавить в язык больше "семантики" - например, сделать более явными решения по совместному использованию объектов (object-sharing). Говорят и о намерениях усилить те особенности языка, которые ориентированы на поддержку приложений в таких областях, как системное программирование, встроенные системы, требующие особо эффективного кода системы?

Безусловно, на С++ влияет общая тенденция к более декларативному стилю программирования. Однако появившееся официальное определение языка вкупе со стандартной библиотекой должно воспрепятствовать внесению изменений в язык, что дает всем заинтересованным сторонам - разработчикам, пользователям, создателям сред программирования, преподавателям - возможность поработать, наконец, с определившимся языком. Естественно, эксперименты будут продолжаться (хотя я вряд ли приму в этом участие), однако, повторю, по моему мнению, сообщество С++ нуждается сейчас в стабильности языка больше, чем в чем-либо ином. Нынешний С++ - законченный и сбалансированный язык; его дальнейшее развитие будет в основном продолжаться уже не в индустрии, а в академической среде.

Думаю, что еще далеко не все оценили по достоинству механизм шаблонов, полезный для различных форм специфицирования. Рассмотрим для примера один набросок, показывающий, как можно определить список так, что единая реализация разделяется всеми списками указателей:

// general list: template class list 
{ /* ... */ }; 
// specialization for lists of void*: template<> class list { /* ... */ }; 
// general list of pointers (implemented using list): 
template class list : list { /* ... */ }; 

Используемый здесь механизм специализации позволяет выбирать различные реализации (на основе дедукции типа), одновременно обеспечивая пользователя единственным общим интерфейсом. Заметьте, этот механизм усиливает декларативную природу программирования на С++, в то же время упрощая пользовательский интерфейс и повышая производительность исполнения. Такие методы, используемые в стандартной библиотеке, позволяют обеспечить единую процедуру sort(), которая для реальных примеров превосходит по эффективности qsort() из стандартной библиотеки Си в 7 раз!

?: Никлаус Вирт [7] охарактеризовал С++ как "язык, который не поощряет структурированное мышление и дисциплинированное конструирование программ". Не могу сказать, что согласен с этим утверждением или что Oberon обеспечивает больше структурированности и дисциплины, чем С++, но есть ли здесь что-либо, в чем бы Вы признали правоту ученых пуристов, которые должны решать, как и на чем им обучать студентов: выбрать ли С++, поскольку он доминирует в мире реальных разработок или не использовать этот язык в учебном процессе, потому что он слишком далек от формального подхода, основанного на специфицировании проблем, часто используемого в процессе преподавания информатики. Так они приходят к Eiffel, который поддерживает "контрактное программирование" или выбирают Smalltalk, "чистый объектно-ориентированный язык".

Профессора Вирта никто не может заподозрить в излишней щедрости на похвалу языков, которые не он спроектировал, поэтому я не могу сказать, что меня очень уж удивляют его оценки. Если же говорить по сути, то он совершенно не прав. С++ - это более чем адекватный инструмент для надлежащего проектирования, для программирования в индустриальных масштабах и даже для точно сфокусированного мышления о серьезных проблемах. Кстати, этот момент нашего разговора по-моему очень подходит для того, чтобы выразить благодарность проектировщикам Simula и Си, обеспечившим хороший базис для построения С++ (кроме того, они просто очень приятные люди). С большой пользой я изучал и многие другие языки, и не случайно в С++ можно найти следы Algol'68, ML, Ada, BCPL. Вообще, существует немало действительно замечательных языков программирования. Кстати сказать, в жизни очень полезно владеть несколькими языками - это верно и для языков программирования.

Многие средства С++ могли бы быть сделаны лучше. Однако так можно сказать о любом реально используемом языке, даже таком, о котором говорят, что он "концептуально чистый". Мой богатый опыт позволяет утверждать, что проблемы с С++ не столь серьезны, чтобы служить препятствием как в учебном процессе, так и на практике. Конечно, студент может оказаться неспособным освоить язык как следует, да и преподаватели бывают всякие. Но это касается любого языка. Преимущество С++ как раз в том, что он используется в самых разных предметных областях и при этом способен настраиваться на масштаб решаемой проблемы. Большинство из тех языков, о которых говорят, что они более "чистые" и их легче осваивать, спроектированы с существенными упрощениями, что не позволяет задействовать их за пределами той - обычно достаточно узкой - области, где эти языки действительно можно применять. От этого не застрахован и С++, но его домен, охватывающий все, что можно обозначить как "программирование систем", весьма широк. С++ имеет более простые "чистые" подмножества, а сложность появляется, когда пользователи начинают играть с такими особенностями и стилями программирования ("парадигмами"), которые требуют более глубокого понимания. Именно здесь пользователи "более чистых" языков часто вынуждены обращаться к альтернативным - более низкоуровневым и "нечистым" языкам - обычно Си или С++. По моему мнению, С++ безусловно должен преподаваться, причем с сильным акцентом на концепции и понятия.

?: Однако все-таки можно представить более "чистое" подмножество Вашего языка, скажем, "студенческий С++", в котором, например, массив не будет приводиться к указателю, если программист об этом специально не просит; можно ввести и другие ограничения в том же духе. Не кажется ли Вам, что таким образом и можно было бы получить отличный обучающий инструмент (а, возможно, и практический - для тех, кто не связан необходимостью выдерживать совместимость с Си)? Или Вы полагаете, что это внесло бы еще большую путаницу?

Я был бы рад увидеть такой "студенческий С++", где встроенные массивы не использовались бы вообще. Вместо них студенты могли бы применять классы "vector", "list", "string" из предоставляемой преподавателем библиотеки (основанной, без сомнения, на стандартной библиотеке). Это легко можно сделать - даже без внесения изменений в компилятор (просто уменьшите уровень grade, использованный при задании встроенных массивов). Точно так же, преподаватель мог бы легко запретить явные преобразования типов; студентам начального уровня обучения просто нет смысла использовать их в своем коде. Вообще, наиболее сложная часть при изучении С++ - да и любого другого языка - это освоение новых методов проектирования и программирования, а не языковых средств, необходимых для их выражения.

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

В учебном процессе С++ постоянно страдает от своей тесной связи с Си. Так как С++ - это (почти) расширение Си, многие думают, что перед изучением С++ они должны освоить (почти) все возможности Си. А это не так - С++, по сравнению с Си, во многих случаях предлагает качественно иные (и лучшие) средства; так, используя библиотеки, можно избежать сложных для многих студентов присущих Си проблем с манипуляциями указателями, приведением типов, массивами и т.п. Так что С++ может быть отличным языком для обучения программированию, стилям программирования, проектированию и т.д. Однако, еще раз подчеркну, необходимо отличать обучение программированию от обучения языкам программирования. Помимо прочего, это позволяет избегать нелепых "языковых войн", на которые тратится столько сил и времени.

Сила С++ как хорошего языка для обучения еще и в том, что в процессе его изучения можно освоить целый ряд методов проектирования и программирования. Альтернатива - обучать нескольким "более чистым" языкам, чтобы проиллюстрировать тот же набор методов. Кстати, мне кажется совершенно неправильным представлять некий стиль проектирования и программирования как единственно верный, что обычно происходит, когда преподается один язык. Профессиональный программист, так же как и исследователь в области компьютерных наук, должен чувствовать себя комфортно с С++, ML, Smalltalk, Lisp, Eiffel - можно было бы упомянуть и другие языки. Естественно, немногие способны быть одновременно экспертами в более чем одном-двух из перечисленных языков, но стоит познакомиться со всеми и попытаться выполнить на них реальные проекты.

?: Реализованный в С++ механизм исключений (exceptions) имеет репутацию тяжелого для корректного использования. Том Каргилл [8] критиковал "необходимость" введения "auto_ptr" в стандартную библиотеку для того, чтобы сделать указатели более управляемыми в присутствии исключений; говорят и о несоответствии (mismatch) между шаблонами (templates) и спецификациями исключений. Есть здесь и трудности с реализацией. Так, Borland имел серьезные проблемы при связывании вместе библиотек DLL даже без обработки исключительных ситуаций. Не буду упоминать споры о "retry" - Вы хорошо осветили эту проблему в работе [1]. Не делают ли исключения кривую обучения языку слишком крутой?

Каждая новая особенность языка поначалу кажется тяжелой в использовании, дорогостоящей и даже не очень нужной. Не думаю, что проблемы, о которых Вы говорите, действительно можно считать реальными проблемами в программировании. Основываясь на собственной практике, могу сказать, что исключения весьма упрощают код. Подобно всем по-настоящему ценным особенностям, исключения требуют от программиста по-новому выстроенного мышления и, соответственно, новых способов организации кода (а если это не так, то как можно считать новую особенность действительно заслуживающей включения в язык?). Что же касается так называемого несоответствия между исключениями и шаблонами, то на самом деле это лишь видимость. Исключения предназначены для построения своеобразных "брандмауэров", способных работать в контексте некоторых условий возникновения ошибок. Вы выбираете определенный интерфейс и определяете, для какого подмножества условий возникновения ошибок он прозрачен. Что же до шаблонов, то почти все они не слишком подходят для брандмауэров. Шаблоны были специально спроектированы в расчете на очень тесное взаимодействие с типами, определенными пользователем, и если Вы сами не лишены здравого смысла, то Вы не будете строить брандмауэр прямо на основе такого "перегруженного смыслами" кода. Если это рассматривать как "mismatch", что ж, пусть будет так. Однако я вижу здесь просто независимость понятий. Они делают разные вещи и могут использоваться в комбинации.

?: И все же до некоторой степени несоответствие между шаблонами и "спецификациями исключений" имеется - шаблоны используют фактические параметры и почти всегда просто невозможно получить корректную спецификацию, даже с использованием невинно выглядящего простого кода (пример - функция-шаблон для сравнения). Я бы сказал, что все-таки с общей точки зрения в механизме исключений есть некая "темная сторона", если даже невинно выглядящий код на самом деле вовсе не столь невинен.

На самом деле масса "невинно выглядящего кода" при ближайшем рассмотрении не обладает этим достоинством. Такой простой код часто содержит много непроверенных условий возникновения ошибок, которые принято обходить в присущем языку Си стиле "setjmp/longjmp". Таким образом, исключения фокусируют внимание на проблеме, которую многие программисты просто предпочитают игнорировать. Однако вовсе не обработка исключений привносит в код сложность - она там уже есть, хотя и невидимая поверхностному взгляду. Я не думаю, что имеет смысл добавлять спецификации исключений к функциям-шаблонам - по крайней мере, только к довольно необычным функциям-шаблонам. Причина, как Вы точно указали, в том, что потенциальные исключительные ситуации - это те, которые генерируются шаблоном плюс те, которые генерируются аргументами шаблона. Вот, в частности, почему я утверждаю, что шаблоны - не лучшие кандидаты на роль брандмауэров. Не думаю, что имеет смысл "надевать" на каждый небольшой фрагмент кода пуленепробиваемый жилет. Вместо этого я предпочитаю выделять подсистемы и защищать их границы брандмауэрами. Именно так я и использую спецификации исключений.

?: Устойчивость объектов - вот область, где достоинства С++ не очевидны. Существует ряд библиотек/методов, но в большинстве случаев вы приходите к необходимости использования специального препроцессора или некоторой вручную кодируемой функции. Механизм идентификации типа во время исполнения RTTI выглядит перспективным в этом отношении, но если здесь не будет введен некоторый стандарт, то не появится ли еще одно непереносимое расширение?

Я вообще не уверен, что устойчивость - это предмет заботы универсального языка программирования. Разные люди нуждаются в различных типах устойчивых данных с очень разными требованиями по производительности, надежности, контролю доступа и т.д. Думаю, правильнее всего этот вопрос отдать на откуп создателям библиотек и баз данных. Я предпочитаю ограничивать использование препроцессоров и дополнительных лингвистических средств, но иногда они необходимы. По моему мнению, язык программирования вовсе не должен делать все. В любом случае он не может делать все хорошо. Что касается RTTI, то да, этот механизм может помочь в реализации различных сервисов, имеющих отношение к устойчивости и базам данных.

?: Вы - автор одного из наиболее успешных языков, когда-либо созданных. Что бы Вы могли посоветовать тем бесчисленным специалистам, которые чуть ли не каждый день изобретают новый язык?

Исходите из проблемы, которую нужно решить. Полезный язык - это решение хорошо понятого множества проблем, а не просто нечто, удовлетворяющее неким модным критериям того, как должен выглядеть язык программирования. Если вы не имеете действительно серьезной проблемы, которая не может быть решена с использованием любого из существующих языков, то даже и не думайте о проектировании еще одного языка. Помните, что проектирование языка - эта та сфера деятельности, где вероятность неудач составляет почти 100%. И никому не советую этим заниматься, если есть альтернатива. Сопротивляйтесь такой работе. Если же все-таки вы должны проектировать новый язык, то заимствуйте из уже известных языков столько, сколько сможете (не забывая выражать благодарность их авторам). И будьте готовы к неудаче и к очень большой работе - может быть, и преуспеете.

?: Visual Basic - это еще один добившийся огромного успеха язык. Некоторые утверждают, что он обеспечивает то, что С++ в области объектно-ориентированного программирования обещал, но в полной мере не реализовал, а именно: массу встраиваемых (pluggable) компонентов, истинное повторное использование кода - все это, может быть, ценой недостаточно выстроенных инженерных аспектов. Правильно ли, по-вашему, что вопросы совместимости, обеспечивающей согласованную работу различных компиляторов С++, бывают отданы на откуп независимым разработкам, а не являются частью стандарта? Понятно, что любой стандарт на двоичный код ограничил бы свободу разработчиков компиляторов, но в конце концов любой стандарт ограничивает чью-то свободу. Скажем, отказом от поддержки множественного наследования можно добиться большей производительности, но это все-таки не причина, чтобы удалять MI из С++. Почему же к вопросу двоичного стандарта применяется иной подход (хотя, конечно, двоичный стандарт - это только один шаг по направлению к "надлежащим" программным компонентам)?

Язык С++ обеспечивает то, что обещал. Нельзя ожидать, что даже такой язык окажется способен покрыть все вдруг вошедшие в моду и сверх всякой меры рекламируемые при раскрутке какого-то другого языка особенности. С++ - это язык программирования, а не язык спецификации модулей или операционная система. Он, как и другие языки, не может обеспечить всем все. Вы можете строить "встраиваемые компоненты", используя С++, но это не то, для чего он в основном предназначен, а потому - требует усилий. Совместимость - тяжелая проблема. Не все понимают, что только через огромную работу по достижению соглашений между многими организациями, к тому же конкурирующими между собой, могла быть достигнута совместимость фрагментов программ на Си, подвергшихся компиляции с помощью различных компиляторов. Должно быть соглашение о последовательности вызова функций, форматах данных, деталях арифметики с плавающей точкой и т.д. С++ в этом отношении потяжелее, чем Си, но ненамного. Вообще, почти все тяжелые проблемы имеют не технический, а политический характер. Вопрос же множественного наследования стоит совершенно отдельно от любого вопроса, связанного с двоичными стандартами и совместимостью компиляторов С++. Я полагаю, что отсутствие MI (по крайней мере - в ранних версиях SOM) если о чем и говорит, так только о неравнодушии их авторов к Smalltalk и Objective C. В языках же, подобных С++, которые полагаются на статическую проверку типов интерфейсов, некоторая форма множественного наследования весьма существенна. Альтернатива - деформированный код, небезопасный интерфейс, или все это вместе.

?: Могли бы Вы назвать какие-то новые понятия, идеи, особенности, которые могут определить лицо С++ следующего поколения, и вообще, как-то предсказать судьбу С++? Хотя я предполагаю, что прежде всего Вы хотели бы видеть С++ стабильным, пусть на какое-то время.

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

Литература

  1. B. Stroustrup "The Design and Evolution of C++", - Addison-Wesley, Reading, MA, 1994
  2. B. Stroustrup "The C++ Programming Language, Third Edition", - Addison-Wesley, Reading, MA, 1997
  3. M.A. Ellis, B. Stroustrup "The Annotated C++ Reference Manual", - Addison-Wesley, Reading, MA, 1990
  4. B.Kernighan, D.Ritchie "The C Programming Language, Second Edition", - Prentice Hall, 1988
  5. M. Sakkinen, "A Critique of the Inheritance Principles of C++", Journal of Computing Systems, Vol. 5 #. 1, Winter 1992.
  6. M. Sakkinen, "The Darker Side of C++ Revisited", Journal of Structured Programming, 1992.
  7. Н. Вирт. "Долой Жирные Программы". Открытые Системы, # 6 (20), 1996, с. 27-31
  8. T. Cargill, "Exception handling: A false sense of security", C++ Report, Vol. 6 #. 9, November-December 1994.

Поделитесь материалом с коллегами и друзьями