Templated-Typen sollen einem "Konzept" (Input Iterator, Forward Iterator usw.) folgen, bei dem die tatsächlichen Details des Konzepts vollständig durch die Implementierung der Vorlagenfunktion / -klasse und nicht durch die Klasse des Typs definiert werden wird mit der Vorlage verwendet, die eine etwas gegen die Verwendung von OOP ist.
Ich denke, Sie verstehen die beabsichtigte Verwendung von Konzepten durch Vorlagen falsch. Forward Iterator zum Beispiel ist ein sehr genau definiertes Konzept. Informationen zu den Ausdrücken, die gültig sein müssen, damit eine Klasse ein Vorwärtsiterator ist, und zu ihrer Semantik einschließlich der Komplexität der Berechnungen finden Sie im Standard oder unter http://www.sgi.com/tech/stl/ForwardIterator.html (Sie müssen den Links zu Input, Output und Trivial Iterator folgen, um alles zu sehen).
Dieses Dokument ist eine perfekte Schnittstelle, und "die tatsächlichen Details des Konzepts" werden genau dort definiert. Sie werden nicht durch die Implementierungen von Vorwärtsiteratoren definiert, und sie werden auch nicht durch die Algorithmen definiert, die Vorwärtsiteratoren verwenden.
Es gibt drei Unterschiede im Umgang mit Schnittstellen zwischen STL und Java:
1) STL definiert gültige Ausdrücke unter Verwendung des Objekts, während Java Methoden definiert, die für das Objekt aufrufbar sein müssen. Natürlich kann ein gültiger Ausdruck ein Methodenaufruf (Elementfunktion) sein, muss es aber nicht sein.
2) Java-Schnittstellen sind Laufzeitobjekte, während STL-Konzepte zur Laufzeit selbst mit RTTI nicht sichtbar sind.
3) Wenn Sie die erforderlichen gültigen Ausdrücke für ein STL-Konzept nicht gültig machen, wird ein nicht angegebener Kompilierungsfehler angezeigt, wenn Sie eine Vorlage mit dem Typ instanziieren. Wenn Sie eine erforderliche Methode einer Java-Schnittstelle nicht implementieren können, wird ein bestimmter Kompilierungsfehler angezeigt.
Dieser dritte Teil ist, wenn Sie eine Art (zur Kompilierungszeit) "Ententypisierung" mögen: Schnittstellen können implizit sein. In Java sind Schnittstellen etwas explizit: Eine Klasse "ist" Iterable genau dann, wenn sie besagt, dass sie Iterable implementiert. Der Compiler kann überprüfen, ob die Signaturen seiner Methoden alle vorhanden und korrekt sind, aber die Semantik ist immer noch implizit (dh sie sind entweder dokumentiert oder nicht, aber nur mehr Code (Komponententests) kann Ihnen sagen, ob die Implementierung korrekt ist).
In C ++ sind wie in Python sowohl Semantik als auch Syntax implizit, obwohl Sie in C ++ (und in Python, wenn Sie den Präprozessor mit starker Typisierung erhalten) Hilfe vom Compiler erhalten. Wenn ein Programmierer eine Java-ähnliche explizite Deklaration von Schnittstellen durch die implementierende Klasse benötigt, besteht der Standardansatz darin, Typmerkmale zu verwenden (und Mehrfachvererbung kann verhindern, dass dies zu ausführlich ist). Was im Vergleich zu Java fehlt, ist eine einzelne Vorlage, die ich mit meinem Typ instanziieren kann und die nur dann kompiliert wird, wenn alle erforderlichen Ausdrücke für meinen Typ gültig sind. Dies würde mir sagen, ob ich alle erforderlichen Bits implementiert habe, "bevor ich es benutze". Das ist eine Annehmlichkeit, aber es ist nicht der Kern von OOP (und es testet immer noch nicht die Semantik,
STL kann für Ihren Geschmack ausreichend OO sein oder auch nicht, aber es trennt die Schnittstelle sicher sauber von der Implementierung. Es fehlt Javas Fähigkeit, über Schnittstellen nachzudenken, und es werden Verstöße gegen die Schnittstellenanforderungen unterschiedlich gemeldet.
Sie können der Funktion sagen, dass ... einen Forward Iterator nur anhand seiner Definition erwartet, in der Sie entweder die Implementierung oder die Dokumentation für ...
Persönlich denke ich, dass implizite Typen eine Stärke sind, wenn sie angemessen verwendet werden. Der Algorithmus sagt, was er mit seinen Vorlagenparametern macht, und der Implementierer stellt sicher, dass diese Dinge funktionieren: Es ist genau der gemeinsame Nenner dessen, was "Schnittstellen" tun sollen. Darüber hinaus ist es unwahrscheinlich, dass Sie mit STL beispielsweise std::copy
die Forward-Deklaration in einer Header-Datei finden. Programmierer sollten anhand ihrer Dokumentation und nicht nur anhand der Funktionssignatur herausfinden, was eine Funktion benötigt. Dies gilt für C ++, Python oder Java. Es gibt Einschränkungen, was mit dem Tippen in einer beliebigen Sprache erreicht werden kann, und der Versuch, mit dem Tippen etwas zu tun, was nicht funktioniert (Semantik überprüfen), wäre ein Fehler.
Allerdings benennen STL-Algorithmen ihre Vorlagenparameter normalerweise so, dass klar wird, welches Konzept erforderlich ist. Dies dient jedoch dazu, nützliche zusätzliche Informationen in der ersten Zeile der Dokumentation bereitzustellen und Vorwärtserklärungen nicht informativer zu gestalten. Es gibt mehr Dinge, die Sie wissen müssen, als in den Parametertypen gekapselt werden können. Sie müssen also die Dokumente lesen. (Zum Beispiel in Algorithmen, die einen Eingabebereich und einen Ausgabe-Iterator verwenden, benötigt der Ausgabe-Iterator wahrscheinlich genug "Platz" für eine bestimmte Anzahl von Ausgaben, basierend auf der Größe des Eingabebereichs und möglicherweise den darin enthaltenen Werten. Versuchen Sie, diesen stark einzugeben. )
Hier ist Bjarne zu explizit deklarierten Schnittstellen: http://www.artima.com/cppsource/cpp0xP.html
In Generika muss ein Argument von einer Klasse sein, die von einer Schnittstelle abgeleitet ist (das C ++ - Äquivalent zur Schnittstelle ist eine abstrakte Klasse), die in der Definition des Generikums angegeben ist. Das bedeutet, dass alle generischen Argumenttypen in eine Hierarchie passen müssen. Dies führt zu unnötigen Einschränkungen bei den Designs und erfordert eine unangemessene Voraussicht der Entwickler. Wenn Sie beispielsweise ein Generikum schreiben und ich eine Klasse definiere, können Benutzer meine Klasse nicht als Argument für Ihr Generikum verwenden, es sei denn, ich kenne die von Ihnen angegebene Schnittstelle und habe meine Klasse daraus abgeleitet. Das ist starr.
Wenn Sie es anders herum betrachten, können Sie mit duck tippen eine Schnittstelle implementieren, ohne zu wissen, dass die Schnittstelle vorhanden ist. Oder jemand kann eine Schnittstelle absichtlich so schreiben, dass Ihre Klasse sie implementiert, nachdem er Ihre Dokumente konsultiert hat, um festzustellen, dass er nicht nach etwas fragt, was Sie noch nicht getan haben. Das ist flexibel.