我的意思是,除了它的名字标准模板库(后来演变成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?


当前回答

如何与ForwardIterator*进行比较?也就是说,你如何检查你所拥有的物品是否是你正在寻找的,或者你已经错过了它?

大多数时候,我会使用这样的方法:

void MyFunc(ForwardIterator<MyType>& i)

这意味着我知道I指向MyType的,我知道如何比较它们。虽然它看起来像一个模板,但实际上不是(没有“template”关键字)。

其他回答

最基本的问题是

void MyFunc(ForwardIterator *I);

你如何安全地获取迭代器返回的东西的类型?对于模板,这是在编译时为您完成的。

The concept of separating interface from interface and being able to swap out the implementations is not intrinsic to Object-Oriented Programming. I believe it's an idea that was hatched in Component-Based Development like Microsoft COM. (See my answer on What is Component-Driven Development?) Growing up and learning C++, people were hyped out inheritance and polymorphism. It wasn't until 90s people started to say "Program to an 'interface', not an 'implementation'" and "Favor 'object composition' over 'class inheritance'." (both of which quoted from GoF by the way).

然后,Java出现了内置的垃圾收集器和接口关键字,突然之间,将接口和实现分开变得可行了。不知不觉,这个想法就成为了OO的一部分。c++、模板和STL的出现要早于这一切。

我的理解是,Stroustrup最初更喜欢“oop风格”的容器设计,实际上也没有看到其他的方法。Alexander Stepanov是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.

(在接下来的采访中,他解释了为什么继承和虚拟——也就是面向对象的设计“从根本上存在缺陷,不应该被使用”)。

当Stepanov将他的库展示给Stroustrup时,Stroustrup和其他人付出了巨大的努力,将其纳入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.

模板类型应该紧随其后 一个“概念”(输入迭代器,前进 迭代器,等等… 定义了概念的细节 完全由实现的 模板函数/类,而不是由 类所使用的类型的类 模板,这是一个 反对使用面向对象编程。

我认为您误解了模板对概念的预期用途。例如,前向迭代器是一个定义非常明确的概念。要找到使类成为前向迭代器必须有效的表达式,以及它们的语义(包括计算复杂性),可以查看标准或http://www.sgi.com/tech/stl/ForwardIterator.html(必须按照Input、Output和Trivial Iterator的链接查看所有内容)。

该文档是一个非常好的界面,“概念的实际细节”就定义在那里。它们不是由前向迭代器的实现定义的,也不是由使用前向迭代器的算法定义的。

STL和Java处理接口的方式有三个不同之处:

1) STL使用对象定义了有效的表达式,而Java定义了必须在对象上调用的方法。当然,有效表达式可能是一个方法(成员函数)调用,但这并不一定是必须的。

2) Java接口是运行时对象,而STL概念在运行时是不可见的,即使使用RTTI。

3)如果你不能使STL概念所需的有效表达式有效,当你实例化一些模板时,你会得到一个未指定的编译错误。如果您未能实现Java接口的必要方法,则会得到一个特定的编译错误。

第三部分是如果你喜欢某种(编译时)"duck typing":接口可以是隐式的。在Java中,接口是显式的:当且仅当一个类说它实现了Iterable时,它才是Iterable。编译器可以检查它的方法的签名是否都存在并且正确,但是语义仍然是隐式的(即它们要么被记录下来,要么没有,但只有更多的代码(单元测试)才能告诉你实现是否正确)。

In C++, like in Python, both semantics and syntax are implicit, although in C++ (and in Python if you get the strong-typing preprocessor) you do get some help from the compiler. If a programmer requires Java-like explicit declaration of interfaces by the implementing class, then the standard approach is to use type traits (and multiple inheritance can prevent this being too verbose). What's lacking, compared with Java, is a single template which I can instantiate with my type, and which will compile if and only if all the required expressions are valid for my type. This would tell me whether I've implemented all the required bits, "before I use it". That's a convenience, but it's not the core of OOP (and it still doesn't test semantics, and code to test semantics would naturally also test the validity of the expressions in question).

STL对你来说可能是OO,也可能不是,但它确实把接口和实现清晰地分开了。它确实缺乏Java在接口上进行反射的能力,并且它以不同的方式报告违反接口需求的情况。

你可以告诉函数。期望前向迭代器仅为 看它的定义,你需要看 实现或文档…

Personally I think that implicit types are a strength, when used appropriately. The algorithm says what it does with its template parameters, and the implementer makes sure those things work: it's exactly the common denominator of what "interfaces" should do. Furthermore with STL, you're unlikely to be using, say, std::copy based on finding its forward declaration in a header file. Programmers should be working out what a function takes based on its documentation, not just on the function signature. This is true in C++, Python, or Java. There are limitations on what can be achieved with typing in any language, and trying to use typing to do something it doesn't do (check semantics) would be an error.

That said, STL algorithms usually name their template parameters in a way which makes it clear what concept is required. However this is to provide useful extra information in the first line of the documentation, not to make forward declarations more informative. There are more things you need to know than can be encapsulated in the types of the parameters, so you have to read the docs. (For example in algorithms which take an input range and an output iterator, chances are the output iterator needs enough "space" for a certain number of outputs based on the size of the input range and maybe the values therein. Try strongly typing that.)

以下是Bjarne对显式声明接口的介绍: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.

从另一个角度来看,使用duck类型,您可以在不知道接口存在的情况下实现接口。或者有人可以故意编写一个接口,让你的类实现它,并咨询你的文档,看看他们不会要求任何你没有做过的事情。这是灵活的。

“面向对象编程对我来说只意味着消息传递、状态过程的本地保留、保护和隐藏,以及所有事情的极端后期绑定。它可以在Smalltalk和LISP中完成。可能还有其他系统也有这种可能,但我不知道它们。”——Alan Kay (Smalltalk的创造者)

c++、Java和大多数其他语言都与经典的OOP相去甚远。也就是说,为意识形态争论并不是特别有效。c++在任何意义上都不是纯粹的,所以它实现的功能在当时似乎是有实用意义的。