Kurze Antwort: Sie mischen Konzepte vor der Kompilierung und Konzepte zur Kompilierungszeit , deren Zweck Ähnlichkeiten aufweist. Schnittstellen (abstrakte Klassen und die gesamte Implementierung des Objektorientierungsparadigmas) werden zur Kompilierungszeit erzwungen . Konzepte sind die gleiche Idee, aber im Kontext der generischen Programmierung, die in C ++ VOR der Kompilierungszeit auftritt . Wir haben das letzte Feature noch nicht.
Aber lassen Sie mich von Anfang an erklären.
Lange Antwort:
Tatsächlich sind Konzepte nur eine Sprachverbesserung und "für den Programmierer einfacher" für etwas, das bereits in der Sprache vorhanden ist und das man als "Ententypisierung" bezeichnen könnte.
Wenn Sie einen Typ an eine Vorlagenfunktion übergeben, dh eine generische Funktion, aus der der Compiler beim Aufruf echten (Inline-) Code generiert, muss dieser Typ einige Eigenschaften (Merkmale?) Haben, die im Vorlagencode verwendet werden. Es ist also die Idee, Enten zu tippen, ABER alles wird zur Kompilierungszeit generiert und ausgeführt .
Was passiert, wenn der Typ nicht die erforderlichen Eigenschaften hat?
Nun, der Compiler wird wissen, dass es erst dann ein Problem gibt, wenn der aus der Vorlage generierte Code kompiliert wurde und fehlschlägt. Das bedeutet, dass der Fehler, der generiert wird, ein Fehler im Vorlagencode ist, der dem Programmierer als sein Fehler angezeigt wird. Außerdem enthält der Fehler aufgrund der Metainformationen, die bei der Generierung von Vorlagencode bereitgestellt werden, unzählige Informationen, um zu wissen, um welche Instanziierung der Vorlage es sich handelt.
Einige Probleme damit: Erstens ist Vorlagencode meistens Bibliothekscode, und die meisten Programmierer sind Benutzer von Bibliothekscode, keine Verfasser von Bibliothekscode. Das bedeutet, dass diese Art von kryptischem Fehler wirklich schwer zu verstehen ist, wenn Sie nicht verstehen, wie die Bibliothek geschrieben ist (nicht nur das Design, wie sie wirklich implementiert ist). Das zweite Problem ist, dass selbst wenn der Programmierer den Vorlagencode geschrieben hat, die Fehlerursachen möglicherweise immer noch unklar sind, da der Compiler erkennen kann, dass ein Problem zu spät vorliegt: Wenn der generierte Code kompiliert wird. Wenn das Problem mit den relativen Typ Eigenschaften, dann sollte es sie prüft , noch bevor Sie den Code zu generieren.
Dies ist es, was Konzepte zulassen (und für die sie entwickelt wurden): Damit der (generische Code-) Programmierer die Eigenschaften von Typen angeben kann, die als Vorlagenparameter übergeben werden, und der Compiler dann explizite Fehler angeben kann, falls die bereitgestellten Typen die Anforderungen nicht erfüllen Anforderungen.
Sobald die Prüfung erfolgreich ist, wird der Code aus der Vorlage generiert und dann mit Sicherheit erfolgreich kompiliert.
Die gesamte Konzeptprüfung erfolgt ausschließlich vor der Kompilierungszeit . Es überprüft die Typen selbst und nicht die Objekttypen . Vor der Kompilierungszeit ist kein Objekt vorhanden.
Nun zu "Schnittstellen".
Wenn Sie einen abstrakten oder virtuellen Basistyp erstellen, erlauben Sie Code, damit Objekte der untergeordneten Typen zu bearbeiten, ohne deren tatsächliche Implementierung zu kennen. Um dies zu erzwingen, macht der Basistyp Mitglieder verfügbar, die virtuell sind und möglicherweise von den untergeordneten Typen überlastet werden (oder müssen).
Dies bedeutet, dass der Compiler zur Kompilierungszeit überprüfen kann, ob alle Objekte, die an eine Funktion übergeben werden, die einen Verweis auf die Basisklasse erfordert, 1. einem der untergeordneten Typen der Basisklasse angehören müssen, 2. dass der untergeordnete Typ Implementierungen von haben muss ggf. in Basisklassen deklarierte virtuelle reine Funktionen.
So zum Zeitpunkt der Kompilierung , prüft der Compiler die Schnittstellen des Objekts Typen und Bericht erstellen , wenn etwas fehlt.
Es ist die gleiche Idee wie bei Concepts, aber es kommt zu spät , wie in der Concept-Beschreibung angegeben. Es tritt zur Kompilierungszeit auf. Wir befinden uns nicht im generischen Code (Vorlagencode), sondern erst, nachdem er verarbeitet wurde, und es ist bereits zu spät, um zu überprüfen, ob die Typen generische Anforderungen erfüllen, die von virtuellen Basisklassen nicht verfügbar gemacht werden können. Tatsächlich existiert die gesamte Implementierung des Objektorientierungsparadigmas in C ++ nicht einmal, wenn der Vorlagencode verarbeitet wird. Es gibt (noch) keine Objekte. Das ist
Klassen beschreiben Einschränkungen für Objekte, die zum Überprüfen der Anforderungen für Funktionen verwendet werden sollen, die diese Objekte bearbeiten. Konzepte beschreiben Einschränkungen für Typen (einschließlich Klassen), die zum Überprüfen der Anforderungen für generischen Code verwendet werden sollen, um aus diesen Typen und der Kombination aus generischem Code echten Code zu generieren.
Es handelt sich also wieder um dieselbe "Überprüfung der geistigen Gesundheit", jedoch in einer anderen Sprachebene, dh um Vorlagen. Vorlagen sind eine vollständige (vollständig) Sprache, die Metaprogrammierung und Programmiertypen ermöglicht, noch bevor sie im kompilierten Code erscheinen. Es ist ein bisschen wie das Schreiben von Skripten für den Compiler. Angenommen, Sie können ein Skript erstellen. Klassen sind nur vom Skript manipulierte Werte. Derzeit gibt es keine andere Möglichkeit, Einschränkungen für diese Werte zu überprüfen, als das Skript auf nicht offensichtliche Weise zum Absturz zu bringen. Konzepte sind genau das: Geben Sie die Eingabe für diese Werte ein (die im generierten Code Typen sind). Ich bin mir nicht sicher, ob ich klar bin ...
Ein weiterer wirklich wichtiger Unterschied zwischen virtuellen Basisklassen und Konzepten besteht darin, dass die erste eine starke Beziehung zwischen Typen erzwingt und sie "blutgebunden" macht. Während die Metaprogrammierung von Vorlagen das "Enten-Tippen" ermöglicht, erlauben Konzepte nur, die Anforderungen klarer zu machen.
Die einfache Antwort auf praktisch alle Ihre Fragen lautet: "Weil C ++ - Compiler scheiße sind". Ernsthaft. Sie basieren auf der Translation Unit-Technologie von C, die viele nützliche Dinge effektiv verbietet, und die vorhandenen Vorlagenimplementierungen sind so schrecklich langsam wie sie sind. Konzepte wurden aus konzeptionellen Gründen nicht gekürzt - sie wurden gekürzt, weil es keine zuverlässige Implementierung gab, ConceptGCC extrem langsam war und das Spezifizieren von Konzepten absurd lange dauerte. Herb Sutter gab an, dass die Angabe der in der Standardbibliothek verwendeten Konzepte mehr Platz beanspruchte als die Angabe der gesamten Vorlagen.
Wahrscheinlich zwischen SFINAE
decltype
undstatic_assert
, sie sind größtenteils implementierbar, wie es jetzt sowieso ist.quelle
Nach meinem Verständnis haben Schnittstellen und Konzepte in verschiedenen Teilen der C ++ - Sprache ähnliche Zwecke.
Wie in der Antwort auf die ursprüngliche Frage erwähnt: Die Implementierung einer Schnittstelle wird vom Implementierer einer Klasse zur Entwurfszeit entschieden. Sobald eine Klasse veröffentlicht wurde, kann sie nur die Schnittstellen unterstützen, von denen sie zur Entwurfszeit abgeleitet wurde.
Zwei unterschiedliche Schnittstellen mit genau denselben Elementfunktionen und Semantik (dh demselben Konzept) sind weiterhin zwei unterschiedliche Schnittstellen. Wenn Sie die Semantik beider Schnittstellen unterstützen möchten, müssen Sie die Unterstützung möglicherweise zweimal implementieren.
Das ist das Problem, das die generische Programmierung umgehen soll. In der generischen C ++ - Programmierung muss der an eine Vorlage übergebene Typ lediglich die Schnittstelle (nicht großgeschrieben im Sinne der "Programmierschnittstelle" eines Typs) unterstützen, die von der Vorlage verwendet wird. Die zwei unterschiedlichen Schnittstellen mit denselben Elementfunktionen stimmen überein, und Sie müssen den Code nur einmal schreiben. Darüber hinaus funktionieren alle Typen (auch ohne explizite Schnittstellen), die dieselbe Schnittstelle unterstützen.
Dies führt zu einem zweiten Problem: Was ist, wenn Sie zwei Typen mit überlappenden Schnittstellen, aber unterschiedlicher Semantik haben? Generische Programmierung wird den Unterschied nicht erkennen können und alles wird gut kompiliert, aber das Laufzeitergebnis wird überraschend und wahrscheinlich falsch sein.
Hier kommen Konzepte ins Spiel. Wenn Sie stark vereinfachen, können Sie ein Konzept als generische (Vorlagen-) Version einer Schnittstelle betrachten. Es muss nur einmal implementiert werden, um auf eine große Anzahl potenzieller Typen angewendet zu werden, die aus dem Konzept "abgeleitet" werden können. Ein Konzept ist eine vorgegebene semantische Schnittstelle eines (Klassen-) Typs, die nicht nur auf diesen Typ beschränkt ist. Es unterscheidet sich von einer Schnittstelle darin, dass zwei sehr unterschiedliche Typen (für den generischen Compiler) immer noch dasselbe Konzept haben können, ohne auf Einschränkungen der Basisklasse oder auf Schnittstellen der Basisklasse zurückgreifen zu müssen, für die keine vom Compiler sichtbare Semantik vorliegt eigene, werden aber nur zur Typunterscheidung verwendet.
quelle