我的意思是,除了它的名字标准模板库(后来演变成c++标准库)。
c++最初将面向对象的概念引入到C中,也就是说:您可以根据特定实体的类和类层次结构来判断它能做什么和不能做什么(不管它是如何做的)。由于多重继承的复杂性,一些能力的组合更难以用这种方式描述,而且c++以一种有点笨拙的方式支持仅接口继承(与java等相比),但它确实存在(并且可以改进)。
然后模板开始发挥作用,还有STL。STL似乎采用了经典的OOP概念,并将它们冲入下水道,取而代之的是使用模板。
当模板用于泛化类型时,类型本身与模板的操作无关(例如容器),这两种情况之间应该有区别。vector<int>非常有意义。
然而,在许多其他情况下(迭代器和算法),模板化类型应该遵循一个“概念”(Input Iterator, Forward Iterator,等等),其中概念的实际细节完全由模板函数/类的实现定义,而不是由模板使用的类型的类定义,这在某种程度上是对OOP的反使用。
例如,你可以告诉函数:
void MyFunc(ForwardIterator<...> *I);
更新:由于在最初的问题中不清楚,ForwardIterator本身可以被模板化以允许任何ForwardIterator类型。相反,将ForwardIterator作为一个概念。
只有通过查看它的定义才能期望Forward Iterator,在这里你需要查看以下方面的实现或文档:
template <typename Type> void MyFunc(Type *I);
我可以提出两个主张来支持使用模板:通过为每个使用的类型重新编译模板,而不是使用动态分派(主要通过虚表),可以使编译后的代码更加高效。2. 事实上,模板可以与原生类型一起使用。
然而,我正在寻找一个更深刻的原因,放弃经典的面向对象的支持模板的STL?
暂时,让我们把标准库看作基本上是集合和算法的数据库。
如果您研究过数据库的历史,那么您无疑知道,在最初,数据库大多是“分层的”。分层数据库与经典的面向对象编程(OOP)非常相似——特别是单一继承类型,例如Smalltalk所使用的那种。
随着时间的推移,分层数据库显然可以用于建模几乎任何东西,但在某些情况下,单继承模型相当有限。如果你有一扇木门,你就可以很方便地把它看作一扇门,或者看作一块原材料(钢铁、木材等)。
因此,他们发明了网络模型数据库。网络模型数据库非常接近于多重继承。c++完全支持多重继承,而Java只支持有限的形式(只能继承一个类,但也可以实现任意多的接口)。
层次模型和网络模型数据库都已经从通用用途中消失了(尽管有一些仍然在相当特定的利基领域)。在大多数情况下,它们已经被关系数据库所取代。
关系数据库取代它的主要原因是它的多功能性。关系模型在功能上是网络模型的超集(网络模型又是层次模型的超集)。
c++在很大程度上遵循了同样的路径。单继承与层次模型、多继承与网络模型的对应关系较为明显。c++模板和层次模型之间的对应关系可能不太明显,但无论如何都非常接近。
我还没有看到它的正式证明,但我相信模板的功能是多重继承所提供的功能的超集(这显然是单一惯性的超集)。一个棘手的部分是模板大多是静态绑定的——也就是说,所有绑定都发生在编译时,而不是运行时。因此,正式证明继承提供了继承功能的超集可能有些困难和复杂(甚至可能是不可能的)。
在任何情况下,我认为这是c++不为它的容器使用继承的主要真正原因——没有真正的理由这样做,因为继承只提供模板所提供的功能的一个子集。由于模板在某些情况下基本上是必需的,因此它们几乎可以在任何地方使用。
STL最初的目的是提供一个涵盖最常用算法的大型库,其目标是一致的行为和性能。模板是使实现和目标可行的关键因素。
只是为了提供另一个参考:
1995年3月,《DDJ》的艾尔·史蒂文斯采访亚历克斯·斯捷潘诺夫:
http://www.sgi.com/tech/stl/drdobbs-interview.html
Stepanov解释了他的工作经验和对大型算法库的选择,最终演变为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.