Вопрос по c++, stl, templates, oop – Почему C ++ STL так сильно основан на шаблонах? (а не на * интерфейсах *)

207

Я имею в виду, кроме его обязательного имени (Стандартная библиотека шаблонов) ...

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

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

Должно быть различие между случаями, когда шаблоны используются для обобщения типов, когда сами типы типов не имеют отношения к работе шаблона (контейнеры, например). Иметьvector<int> имеет смысл.

Однако во многих других случаях (итераторы и алгоритмы) предполагается, что шаблонные типы должны следовать «концепции» (Input Iterator, Forward Iterator и т. Д.), Где фактические детали концепции полностью определяются реализацией функции / класса шаблона, а не классом типа, используемого с шаблоном, что несколько противоречит -использование ООП.

Например, вы можете сказать функцию:

<code>void MyFunc(ForwardIterator<...> *I);
</code>

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

ожидает прямого итератора, только взглянув на его определение, где вам нужно либо посмотреть на реализацию, либо на документацию для:

<code>template <typename Type> void MyFunc(Type *I);
</code>

Я могу сделать два утверждения в пользу использования шаблонов: скомпилированный код можно сделать более эффективным путем индивидуальной компиляции шаблона для каждого используемого типа вместо использования vtables. И тот факт, что шаблоны могут использоваться с нативными типами.

Однако я ищу более глубокую причину, почему отказываться от классического ООП в пользу шаблонов для STL? (Предполагая, что вы читали это далеко: P)

и нет никаких причин, почему избыточный код будет заполнять icache. Если я создаю экземпляр вектора & lt; char & gt; и вектор & lt; int & gt; почему в моей программе должен использоваться вектор & lt; char & gt; код будет загружен в icache во время обработки вектора & lt; int & gt ;? Фактически, код для вектора & lt; int & gt; обрезается, потому что он не должен включать код для приведения, vtables и косвенного обращения. jalf
@Jonas: Это не имеет смысла. Ограничение на кеш стоит тактов, поэтому это важно. В конце концов, именно такты, а не кэш, определяют производительность. Память и кеш важны только в той мере, в которой они влияют на затраченные такты. Более того, эксперимент можно сделать легко. Сравните, скажем, std :: for_Each, вызываемый с аргументом функтора, с эквивалентным подходом OOP / vtable. Разница в производительностиstaggering, Именно поэтому используется версия шаблона. jalf
Алекс Степановexplains почему наследование и равенство плохо сочетаются друг с другом. fredoverflow
Вы можете проверитьstackoverflow.com/questions/31693/…, Принятый ответ является отличным объяснением того, что шаблоны предлагают вам по сравнению с дженериками. James McMahon
@BerndJendrissek: Хм, близко, но не вы сами. Да, больше кода стоит с точки зрения пропускной способности памяти и использования кэшаif it is ever actually used, Но нет особой причины ожидатьvector<int> а такжеvector<char> использоваться одновременно. Они могут, конечно, но вы могли бы использоватьany две части кода одновременно. Это не имеет ничего общего с шаблонами, C ++ или STL. Там нет ничего в реализацииvector<int> что требуетvector<char> код для загрузки или исполнения. jalf

Ваш Ответ

13   ответов
582

еще в конце 70-х Stroustrup намеревался создать модернизированный C с возможностями ООП, но это давно. К тому времени, когда язык был стандартизирован в 1998 году, он больше не был языком ООП. Это был мультипарадигмальный язык. Он, конечно, имел некоторую поддержку кода ООП, но он также имел наложенный на тьюринг язык шаблонов, он позволял метапрограммировать во время компиляции, и люди открыли универсальное программирование. Внезапно ООП кажется не таким уж важным. Не тогда, когда мы можем написать проще, более краткимand более эффективный код с использованием методов, доступных через шаблоны и общее программирование.

ООП не святой Грааль. Это милая идея, и она значительно улучшилась по сравнению с процедурными языками еще в 70-х годах, когда она была изобретена. Но, честно говоря, это еще не все. Во многих случаях это неуклюже и многословно, и это действительно не продвигает повторно используемый код или модульность.

Вот почему сообщество C ++ сегодня гораздо больше интересуется общим программированием, и почемуeveryone наконец начинают понимать, что функциональное программирование также достаточно умно. ООП само по себе просто не очень красиво.

Попробуйте нарисовать график зависимости гипотетического «OOP-ified» STL. Сколько классов нужно знать друг о друге? Там будетlot зависимостей. Вы могли бы включить толькоvector заголовок, также не получаяiterator или дажеiostream вытащил? STL делает это легко. Вектор знает о типе итератора, который он определяет, и это все. Алгоритмы STL знаютnothing, Им даже не нужно включать заголовок итератора, хотя все они принимают итераторы в качестве параметров. Что является более модульным?

STL может не следовать правилам ООП, как его определяет Java, но не достигаетgoals ООП? Разве это не обеспечивает возможность повторного использования, низкое сцепление, модульность и инкапсуляцию?

И это не достигает этих целейbetter чем версия с ООП?

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

Сначала шаблоны были добавлены в C ++. Они были добавлены по той же причине, что дженерики были добавлены в .NET. Казалось хорошей идеей иметь возможность писать такие вещи, как & quot; контейнеры типа T & quot; без выбрасывания типа безопасности. Конечно, реализация, на которой они остановились, была намного более сложной и мощной.

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

Он представил его комитету по языку C ++, которому понадобилось немало времени, чтобы привыкнуть к нему, потому что он выглядел так странно и по-другому, но в конце концов понял,it worked better than the traditional OOP equivalents they'd have to include otherwise, Таким образом, они внесли несколько изменений в него и приняли его в стандартную библиотеку.

Это был не идеологический выбор, это был не политический выбор «хотим мы быть ООП или нет», а очень прагматичный. Они оценили библиотеку и увидели, что она работает очень хорошо.

В любом случае, обе причины, упомянутые вами в пользу STL, абсолютно необходимы.

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

И STLhas работать с примитивными типами, потому что примитивные типы - это все, что у вас есть в C, и они составляют основную часть обоих языков. Если бы STL не работал с собственными массивами, это было быuseless.

Ваш вопрос убедительно предполагает, что ООП является "лучшим". Мне любопытно услышать почему. Вы спрашиваете, почему они "отказались от классического ООП". Мне интересно, почему они должны были придерживаться этого. Какие преимущества это имело бы?

Это хорошая запись, но я хотел бы выделить одну деталь. STL не является "продуктом" C ++. Фактически, STL, как концепция, существовал до C ++, а C ++ оказался эффективным языком, обладающим (почти) достаточными возможностями для универсального программирования, поэтому STL был написан на C ++.
@MasonWheeler, если бы этот ответ был просто вопиющей чепухой, вы бы не увидели, что сотни разработчиков по всему миру проголосовали бы +1 за это, и только три человека сделали иначе.
@einpoklum, пожалуйста, взгляните на язык Smalltalk, который Алан Кей разработал как язык ООП, когда изобрел термин ООП. У него не было интерфейсов. ООП не об интерфейсах или абстрактных базовых классах. Собираетесь ли вы сказать, что «Java», который не имеет ничего общего с тем, что имел в виду изобретатель термина ООП, является скорее ООП, чем С ++, которыйalso не имеет ничего общего с тем, что имел в виду изобретатель термина ООП? То, что вы хотите сказать, это "C ++ не достаточно Java-подобен на мой вкус". Это справедливо, но имеетnothing делать с ООП.
@einpoklum А что бы вы получили от абстрактного базового класса? приниматьstd::set В качестве примера. Он не наследуется от абстрактного базового класса. Как это ограничивает ваше использованиеstd::set? Есть ли что-то, что вы не можете сделать сstd::set потому что он не наследуется от абстрактного базового класса?
Поскольку комментарии продолжают вызывать это, да, я знаю, что имя STL неоднозначно. Но я не могу придумать лучшего названия для «части стандартной библиотеки C ++, которая смоделирована на STL». Фактическое имя для этой части стандартной библиотекиis просто "STL", даже если это строго неточно. :) Пока люди не используют STL в качестве названия дляentire стандартная библиотека (включая IOStreams и заголовочные файлы C stdlib), я счастлив. :)
0

что шаблоны поддерживают открытый дизайн. В текущем состоянии объектно-ориентированных языков программирования при работе с такими проблемами необходимо использовать шаблон посетителя, и истинный ООП должен поддерживать множественное динамическое связывание. УвидетьОткрытые мульти-методы для C ++, P. Pirkelbauer, et.al. для очень интересного чтения.

Еще одним интересным моментом шаблонов является то, что они могут быть использованы для полиморфизма во время выполнения. Например

template<class Value,class T>
Value euler_fwd(size_t N,double t_0,double t_end,Value y_0,const T& func)
    {
    auto dt=(t_end-t_0)/N;
    for(size_t k=0;k<N;++k)
        {y_0+=func(t_0 + k*dt,y_0)*dt;}
    return y_0;
    }

Обратите внимание, что эта функция также будет работать, еслиValue вектор какой-тоnot std :: vector, который должен быть вызванstd::dynamic_array чтобы избежать путаницы)

Еслиfunc мала, эта функция много выиграет от встраивания. Пример использования

auto result=euler_fwd(10000,0.0,1.0,1.0,[](double x,double y)
    {return y;});

В этом случае вы должны знать точный ответ (2.718 ...), но легко построить простой ODE без элементарного решения (подсказка: используйте полином по y).

Теперь у вас есть большое выражение вfuncи вы используете ODE-решатель во многих местах, поэтому ваш исполняемый файл загрязнен экземплярами шаблонов повсюду. Что делать? Первое, на что нужно обратить внимание, это то, что обычный указатель функции работает. Затем вы хотите добавить карри, чтобы вы написали интерфейс и явную реализацию

class OdeFunction
    {
    public:
        virtual double operator()(double t,double y) const=0;
    };

template
double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction& func);

Но приведенная выше реализация работает только дляdoubleпочему бы не написать интерфейс как шаблон:

template<class Value=double>
class OdeFunction
    {
    public:
        virtual Value operator()(double t,const Value& y) const=0;
    };

и специализируются на некоторых распространенных типах значений:

template double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction<double>& func);

template vec4_t<double> euler_fwd(size_t N,double t_0,double t_end,vec4_t<double> y_0,const OdeFunction< vec4_t<double> >& func); // (Native AVX vector with four components)

template vec8_t<float> euler_fwd(size_t N,double t_0,double t_end,vec8_t<float> y_0,const OdeFunction< vec8_t<float> >& func); // (Native AVX vector with 8 components)

template Vector<double> euler_fwd(size_t N,double t_0,double t_end,Vector<double> y_0,const OdeFunction< Vector<double> >& func); // (A N-dimensional real vector, *not* `std::vector`, see above)

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

5

Основная проблема с

void MyFunc(ForwardIterator *I);

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

Ну, я либо: 1. Не пытаюсь получить его, так как я пишу общий код. Или 2. Получите его, используя любой механизм отражения, который C ++ предлагает сегодня.
84

о чем я думаю, что вы спрашиваете / жалуетесь, заключается в следующем: предположение, что C ++ является языком ООП, является ложным предположением.

C ++ - это мультипарадигмальный язык. Он может быть запрограммирован с использованием принципов ООП, он может быть запрограммирован процедурно, он может быть запрограммирован в общем (шаблоны), а с C ++ 11 (ранее известный как C ++ 0x) некоторые вещи могут быть даже запрограммированы функционально.

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

@ Tyler Действительно, если вы ограничите C ++ до чистого ООП, вы останетесь с Objective-C.
@TylerMcHenry: только что спросилthisЯ считаю, что я только что сказал тот же ответ, что и вы! Всего лишь одно очко. Я хотел бы, чтобы вы добавили тот факт, что стандартную библиотеку нельзя использовать для написания объектно-ориентированного кода.
& quot; а с C ++ 0x некоторые вещи могут даже программироваться функционально & quot; - он может быть запрограммирован функционально без этих функций, просто более подробно.
17

Библиотека алгоритмов была бы лучше ?! ООП не решение для каждой вещи.

ИМХО, STL - самая элегантная библиотека, которую я когда-либо видел :)

на твой вопрос,

вам не нужен полиморфизм во время выполнения, для STL является преимуществом реализовать библиотеку с использованием статического полиморфизма, что означает эффективность. Попробуйте написать общий алгоритм сортировки или расстояния или какой-либо другой алгоритм, который применяется ко ВСЕМ контейнерам! Ваша сортировка в Java будет вызывать функции, которые динамически выполняются через n уровней!

Вам нужны такие глупые вещи, как Boxing и Unboxing, чтобы скрыть неприятные предположения о так называемых языках Pure OOP.

Единственная проблема, которую я вижу с STL и шаблонами в целом, это ужасные сообщения об ошибках. Который будет решен с использованием концепций в C ++ 0X.

Сравнение STL с коллекциями на Java - это все равно, что сравнивать Тадж-Махал с моим домом :)

GCC 4.6 улучшил шаблоны сообщений об ошибках, и я считаю, что 4.7+ еще лучше с ним.
Что, Тадж-Махал маленький и элегантный, а ваш дом размером с гору и в полном беспорядке? ;)
Концепции больше не являются частью c ++ 0x. Некоторые сообщения об ошибках могут быть удалены с помощьюstatic_assert возможно.
Концепция - это, по сути, «интерфейс»; что ОП спрашивал. Единственное отличие состоит в том, что «наследование» из Концепции является неявным (если класс имеет все нужные функции-члены, это автоматически является подтипом Концепции), а не явным (класс Java должен явно объявить, что он реализует интерфейс). Тем не менее, как неявные, так и явные подтипы являются допустимыми OO, и некоторые языки OO имеют неявное наследование, которое работает так же, как Concepts. То, что здесь сказано, в основном "ОО отстой: используйте шаблоны. Но у шаблонов есть проблемы, поэтому используйте Concepts (которые являются OO). & Quot;
23

опрос со Степановым, автором STL:

Yes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people.

@ Кос, в соответствии сweb.archive.org/web/20000607205939/http://www.stlport.org/… первая версия связанной страницы - с 7 июня 2001 года. На самой странице внизу написано Copyright 2001-2008.
Хороший драгоценный камень; Вы знаете, с какого это года?
6

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

Просто чтобы дать еще одну ссылку:

Интервью Аль Стивенса Алекс Степанов, в марте 1995 года DDJ:

http://www.sgi.com/tech/stl/drdobbs-interview.html

Степанов объяснил свой опыт работы и выбор большой библиотеки алгоритмов, которая в конечном итоге превратилась в STL.

Tell us something about your long-term interest in generic programming

.....Then I was offered a job at Bell Laboratories working in the C++ group on C++ libraries. They asked me whether I could do it in C++. Of course, I didn't know C++ and, of course, I said I could. But I couldn't do it in C++, because in 1987 C++ didn't have templates, which are essential for enabling this style of programming. Inheritance was the only mechanism to obtain genericity and it was not sufficient.

Even now C++ inheritance is not of much use for generic programming. Let's discuss why. Many people have attempted to use inheritance to implement data structures and container classes. As we know now, there were few if any successful attempts. C++ inheritance, and the programming style associated with it are dramatically limited. It is impossible to implement a design which includes as trivial a thing as equality using it. If you start with a base class X at the root of your hierarchy and define a virtual equality operator on this class which takes an argument of the type X, then derive class Y from class X. What is the interface of the equality? It has equality which compares Y with X. Using animals as an example (OO people love animals), define mammal and derive giraffe from mammal. Then define a member function mate, where animal mates with animal and returns an animal. Then you derive giraffe from animal and, of course, it has a function mate where giraffe mates with animal and returns an animal. It's definitely not what you want. While mating may not be very important for C++ programmers, equality is. I do not know a single algorithm where equality of some kind is not used.

0

как вы проверяете, что у вас есть товар, который вы ищете, или вы его пропустили?

Большую часть времени я бы использовал что-то вроде этого:

void MyFunc(ForwardIterator<MyType>& i)

это означает, что я знаю, что указываю на MyType, и знаю, как их сравнивать. Хотя он выглядит как шаблон, на самом деле это не так (без ключевого слова «шаблон»).

Вы можете просто использовать & lt ;, & gt; и = операторы типа и не знают, что это такое (хотя это может быть не то, что вы имели в виду)
В зависимости от контекста, они могут не иметь никакого смысла, или они могут работать нормально. Трудно сказать, не зная больше о MyType, что, по-видимому, делает пользователь, а мы - нет.
8

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

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

-1

изаций не является неотъемлемой частью объектно-ориентированного программирования. Я считаю, что это идея, которая была разработана в компонентно-ориентированной разработке, такой как Microsoft COM. (Увидетьмой ответ на что такое компонентно-управляемая разработка?) Когда росли и изучали C ++, люди были лишены наследственности и полиморфизма. Это было до тех пор, пока люди 90-х не начали говорить «Программа для« интерфейса », а не« реализация ». и «композиция объекта Favour»; сверх "наследования классов". " (оба из которых цитируются GoF, кстати).

Затем пришла Java со встроенным сборщиком мусора иinterface Ключевое слово, и внезапно стало практичным фактически отделить интерфейс и реализацию. Прежде чем вы это знаете, идея стала частью ОО. C ++, шаблоны и STL предшествуют всему этому.

Договорились, что интерфейсы не просто ОО. Но способность полиморфизма в системе типов есть (это было в Симуле в 60-х годах). Интерфейсы модулей существовали в Modula-2 и Ada, но я думаю, что они работали в системе типов иначе.
11

a "concept" (Input Iterator, Forward Iterator, etc...) where the actual details of the concept are defined entirely by the implementation of the template function/class, and not by the class of the type used with the template, which is a somewhat anti-usage of OOP.

Я думаю, что вы неправильно понимаете предполагаемое использование концепций шаблонами. Например, прямой итератор - это очень четкое понятие. Чтобы найти выражения, которые должны быть действительными, чтобы класс был прямым итератором, и их семантику, включая сложность вычислений, вы посмотрите на стандарт илиhttp://www.sgi.com/tech/stl/ForwardIterator.html (Вы должны перейти по ссылкам на Input, Output и Trivial Iterator, чтобы увидеть все это).

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

Различия в том, как обрабатываются интерфейсы между STL и Java, имеют три аспекта:

1) STL определяет допустимые выражения, используя объект, тогда как Java определяет методы, которые должны вызываться на объекте. Конечно, допустимым выражением может быть вызов метода (функции-члена), но это не обязательно.

2) Java-интерфейсы являются объектами времени выполнения, тогда как понятия STL не видны во время выполнения даже с RTTI.

3) Если вы не в состоянии сделать правильными требуемые допустимые выражения для концепции STL, вы получите неопределенную ошибку компиляции, когда создаете экземпляр шаблона с типом. Если вам не удастся реализовать требуемый метод интерфейса Java, вы получите конкретную ошибку компиляции, говорящую об этом.

Эта третья часть, если вам нравится своего рода ("время компиляции") интерфейсы: неявные интерфейсы. В Java интерфейсы являются несколько явными: класс "is" Итерируемый, если и только если этоsays он реализует Iterable. Компилятор может проверить, что все сигнатуры его методов присутствуют и являются правильными, но семантика все еще неявна (т.е. они либо документированы, либо нет, но только дополнительный код (модульные тесты) может сказать вам, является ли реализация правильной).

В C ++, как и в Python, семантика и синтаксис неявны, хотя в C ++ (и в Python, если вы получаете препроцессор строгой типизации) вы получаете некоторую помощь от компилятора. Если программисту требуется явное объявление интерфейсов в Java-стиле реализующим классом, тогда стандартным подходом является использование признаков типа (а множественное наследование может предотвратить это слишком многословно). По сравнению с Java не хватает одного шаблона, который я могу создать для своего типа и который будет скомпилирован тогда и только тогда, когда все необходимые выражения будут действительны для моего типа. Это сообщит мне, реализовал ли я все требуемые биты, "прежде чем использовать его". Это удобно, но это не ядро ООП (и оно все еще не проверяет семантику, и код для проверки семантики, естественно, также проверяет достоверность рассматриваемых выражений).

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

you can tell the function ... expects a Forward Iterator only by looking at its definition, where you'd need either to look at the implementation or the documentation for ...

Лично я считаю, что неявные типы являются сильной стороной при правильном использовании. Алгоритм говорит, что он делает со своими параметрами шаблона, и разработчик обеспечивает, чтобы эти вещи работали: он является в точности общим знаменателем того, что "интерфейсы" следует сделать. Кроме того, с STL вы вряд ли будете использовать, скажем,std::copy основанный на поиске его прямого объявления в заголовочном файле. Программистыshould выяснить, что функция принимает на основе своей документации, а не только сигнатуры функции. Это верно в C ++, Python или Java. Существуют ограничения на то, что может быть достигнуто с помощью набора текста на любом языке, и попытка использовать набор для выполнения того, чего он не делает (проверка семантики), будет ошибкой.

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

Вот Бьярне о явно объявленных интерфейсах:http://www.artima.com/cppsource/cpp0xP.html

In generics, an argument must be of a class derived from an interface (the C++ equivalent to interface is abstract class) specified in the definition of the generic. That means that all generic argument types must fit into a hierarchy. That imposes unnecessary constraints on designs requires unreasonable foresight on the part of developers. For example, if you write a generic and I define a class, people can't use my class as an argument to your generic unless I knew about the interface you specified and had derived my class from it. That's rigid.

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

На явно объявленных интерфейсах два слова: классы типов. (Который уже имеет в виду Степанов под «концепцией».)
2

ых коллекций и алгоритмов.

Если вы изучали историю баз данных, вы, несомненно, знаете, что в самом начале базы данных были в основном «иерархическими». Иерархические базы данных очень близко соответствовали классическим ООП, в частности, разновидности с одним наследованием, такой как используется Smalltalk.

Со временем стало очевидно, что иерархические базы данных могут использоваться для моделирования почти всего,but в некоторых случаях модель единственного наследования была довольно ограничивающей. Если у вас была деревянная дверь, было бы удобно смотреть на нее как на дверь или на кусок какого-то сырья (сталь, дерево и т. Д.).

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

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

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

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

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

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

67

Страуструп изначально предпочитал "ООП-стиль" дизайн контейнера, и фактически не видел другого способа сделать это. Александр Степанов является ответственным за STL, иего цели не включали «сделать его объектно-ориентированным»:

That is the fundamental point: algorithms are defined on algebraic structures. It took me another couple of years to realize that you have to extend the notion of structure by adding complexity requirements to regular axioms. ... I believe that iterator theories are as central to Computer Science as theories of rings or Banach spaces are central to Mathematics. Every time I would look at an algorithm I would try to find a structure on which it is defined. So what I wanted to do was to describe algorithms generically. That's what I like to do. I can spend a month working on a well known algorithm trying to find its generic representation. ...

STL, at least for me, represents the only way programming is possible. It is, indeed, quite different from C++ programming as it was presented and still is presented in most textbooks. But, you see, I was not trying to program in C++, I was trying to find the right way to deal with software. ...

I had many false starts. For example, I spent years trying to find some use for inheritance and virtuals, before I understood why that mechanism was fundamentally flawed and should not be used. I am very happy that nobody could see all the intermediate steps - most of them were very silly.

(Он объясняет, почему наследование и виртуальные объекты - например, объектно-ориентированное проектирование - были в корне ошибочными и не должны использоваться «в оставшейся части интервью»).

Как только Степанов представил свою библиотеку Страуструпу, Страуструп и другие предприняли огромные усилия, чтобы привести ее в стандарт ISO C ++ (то же интервью):

The support of Bjarne Stroustrup was crucial. Bjarne really wanted STL in the standard and if Bjarne wants something, he gets it. ... He even forced me to make changes in STL that I would never make for anybody else ... he is the most single minded person I know. He gets things done. It took him a while to understand what STL was all about, but when he did, he was prepared to push it through. He also contributed to STL by standing up for the view that more than one way of programming was valid - against no end of flak and hype for more than a decade, and pursuing a combination of flexibility, efficiency, overloading, and type-safety in templates that made STL possible. I would like to state quite clearly that Bjarne is the preeminent language designer of my generation.

Многие жалобы, которые он предъявляет в отношении таких языков, как Java («Вы не можете написать универсальный max () в Java, который принимает два аргумента некоторого типа и имеет возвращаемое значение того же типа»), относились только к очень раннему версии языка, прежде чем были добавлены дженерики. Даже с самого начала было известно, что дженерики в конечном итоге будут добавлены, хотя (как только будет определен жизнеспособный синтаксис / семантика), поэтому его критика в основном беспочвенна. Да, дженерики в той или иной форме необходимы для сохранения безопасности типов в статически типизированном языке, но нет, это не делает ОО бесполезным.
Одно из самых интересных интервью о программировании, которое я когда-либо читал. Хотя это оставляет меня жаждать подробностей ...
Интересное интервью. Я уверен, что я читал это раньше, но определенно стоило повторить. :)

Похожие вопросы