Verwirrung über statischen CRTP-Polymorphismus

9

Ich versuche meinen Kopf um das CRTP zu wickeln. Es gibt einige gute Quellen, einschließlich dieses Forums, aber ich denke, ich habe etwas Verwirrung über die Grundlagen des statischen Polymorphismus. Blick auf den folgenden Wikipedia-Eintrag:

%Vor%

Ich verstehe, dass dies mir hilft, verschiedene implementation () - Varianten in abgeleiteten Klassen zu haben, ähnlich einer virtuellen Kompilierungsfunktion. Meine Verwirrung ist jedoch, dass ich glaube, dass ich keine Funktionen wie

haben kann %Vor%

wie bei normaler Vererbung und virtuellen Funktionen, da Base als Template verwendet wird, aber ich müsste

angeben %Vor%

oder verwenden

%Vor%

Also, was kauft mir CRTP in diesem Zusammenhang, anstatt einfach die Methode in Derived :: Base zu überschatten / zu implementieren?

%Vor%     
user32849 06.05.2017, 14:20
quelle

3 Antworten

7
___ qstnhdr ___ Verwirrung über statischen CRTP-Polymorphismus ___ antwort43821770 ___

Die Sache ist, dass die Beschreibung von CRTP als "statischer Polymorphismus" nicht wirklich hilfreich oder genau ist, was CRPT betrifft. Beim Polymorphismus geht es eigentlich nur um verschiedene Typen, die dieselbe Schnittstelle oder denselben Vertrag erfüllen; Wie diese verschiedenen Typen diese Schnittstelle implementieren, ist orthogonal zum Polymorphismus. Der dynamische Polymorphismus sieht so aus:

%Vor%

Dabei ist Animal eine Basisklasse, die eine virtuelle make_sound -Methode bereitstellt, die Dog , Cat usw. überschreibt. Hier ist der statische Polymorphismus:

%Vor%

Und das ist es. Sie können die statische Version von foo für jeden Typ aufrufen, der eine make_sound -Methode definiert, ohne von einer Basisklasse zu erben. Und der Anruf wird zur Kompilierungszeit aufgelöst (d. H. Sie zahlen nicht für einen Vtable-Anruf).

Wo passt CRTP? Bei CRTP geht es wirklich nicht um die Schnittstelle, es geht also nicht um Polymorphie. Bei CRTP geht es darum, dass Sie Dinge einfacher implementieren können. Was CRTP magisch macht, ist, dass es Dinge direkt in die Schnittstelle eines Typs injizieren kann, mit vollem Wissen über alles, was der abgeleitete Typ bietet. Ein einfaches Beispiel könnte sein:

%Vor%

Nun kann jeder Klasse, die einen Additionsoperator definiert, auch eine double -Methode zugewiesen werden:

%Vor%

Bei CRTP dreht sich alles um die Unterstützung bei der Implementierung, nicht um die Schnittstelle. Also nicht zu lange darüber auf, dass es oft als "statische Polymorphie" bezeichnet wird. Wenn Sie das echte kanonische Beispiel für die Verwendung von CRTP benötigen, sollten Sie Kapitel 1 von Andrei Alexandrescus Modern C ++ - Design in Betracht ziehen. Aber, nehmen Sie es langsam: -).

    
___ tag123c ___ C ++ ist eine universelle Programmiersprache. Es wurde ursprünglich als Erweiterung von C entworfen und behält eine ähnliche Syntax, ist aber jetzt eine komplett andere Sprache. Verwenden Sie dieses Tag für Fragen zu Code, der mit einem C ++ - Compiler kompiliert werden soll. ___ antwort43821627 ___

Die Vorteile von CRTP werden erst deutlich, wenn mehr als eine Funktion involviert ist. Betrachten Sie diesen Code (ohne CRTP):

%Vor%

Dies gibt aus:

  

0

[Live-Beispiel]

Aufgrund der statischen Natur des C ++ - Typsystems ruft der Aufruf von %code% alle Funktionen von %code% auf. Die versuchten Überschreibungen in %code% werden nicht aufgerufen.

Dies ändert sich, wenn CRTP verwendet wird:

%Vor%

Ausgabe:

  

Wir sind fertig!
  -26

[Live-Beispiel]

Auf diese Weise wird die Implementierung in %code% tatsächlich in %code% aufgerufen, wenn %code% eine "Überschreibung" bereitstellt.

Dies wäre sogar in Ihrem ursprünglichen Code sichtbar: Wenn %code% keine CRTP-Klasse ist, wird der Aufruf von %code% niemals in %code% aufgelöst.

Was die Vorteile von CRTP gegenüber anderen Ansätzen sind:

  • CRTP versus %code% Funktionen:

    CRTP ist ein Kompilierzeitkonstrukt, dh es ist kein Laufzeitaufwand verbunden. Das Aufrufen einer virtuellen Funktion über eine Basisklassenreferenz (normalerweise) erfordert einen Aufruf über einen Zeiger zur Funktion und somit indirekte Kosten und verhindert das Inlining.

  • CRTP versus einfach alles in %code% implementieren:

    Wiederverwendung der Basisklassencodes.

Natürlich ist CRTP ein rein kompilierbares Konstrukt. Um den kompilierbaren Polymorphismus zu erreichen, müssen Sie ein kompilierbares polymorphes Konstrukt verwenden: Templates. Es gibt zwei Möglichkeiten, dies zu tun:

%Vor%

Ersteres entspricht näher dem Laufzeit-Polymorphismus und bietet eine bessere Typensicherheit, letzteres ist mehr auf Duck-Typ basiert.

    
___ qstntxt ___

Ich versuche meinen Kopf um das CRTP zu wickeln. Es gibt einige gute Quellen, einschließlich dieses Forums, aber ich denke, ich habe etwas Verwirrung über die Grundlagen des statischen Polymorphismus. Blick auf den folgenden Wikipedia-Eintrag:

%Vor%

Ich verstehe, dass dies mir hilft, verschiedene implementation () - Varianten in abgeleiteten Klassen zu haben, ähnlich einer virtuellen Kompilierungsfunktion. Meine Verwirrung ist jedoch, dass ich glaube, dass ich keine Funktionen wie

haben kann %Vor%

wie bei normaler Vererbung und virtuellen Funktionen, da Base als Template verwendet wird, aber ich müsste

angeben %Vor%

oder verwenden

%Vor%

Also, was kauft mir CRTP in diesem Zusammenhang, anstatt einfach die Methode in Derived :: Base zu überschatten / zu implementieren?

%Vor%     
___ tag123crtp ___ Das seltsam wiederkehrende Vorlagenmuster (CRTP) ist ein C ++ - Idiom, in dem eine Klasse X von einer Klassenvorlageninstanziierung abgeleitet wird, wobei X selbst als Vorlagenargument verwendet wird. ___ answer44187495 ___

Sie haben Recht, dass weder

%Vor%

oder

%Vor%

gibt Ihnen statischen Polymorphismus. Die erste kompiliert nicht, weil %code% kein Typ und die zweite nicht polymorph ist.

Angenommen, Sie haben zwei abgeleitete Klassen, %code% und %code% . Dann können Sie %code% selbst zu einer Vorlage machen.

%Vor%

Dies kann dann mit jedem Typ aufgerufen werden, der von %code% abgeleitet ist, und es wird der statische Typ des übergebenen Parameters verwendet, um zu entscheiden, welche Funktion aufgerufen werden soll.

Dies ist nur eine der Anwendungen von CRTP, und wenn ich raten würde, würde ich das weniger häufige sagen. Sie können es auch als Nir Friedman in einer anderen Antwort verwenden, die nichts mit statischem Polymorphismus zu tun hat. p>

Beide Anwendungen werden sehr gut diskutiert hier

    
___
Nir Friedman 06.05.2017 14:44
quelle
5
___ qstnhdr ___ Verwirrung über statischen CRTP-Polymorphismus ___ antwort43821770 ___

Die Sache ist, dass die Beschreibung von CRTP als "statischer Polymorphismus" nicht wirklich hilfreich oder genau ist, was CRPT betrifft. Beim Polymorphismus geht es eigentlich nur um verschiedene Typen, die dieselbe Schnittstelle oder denselben Vertrag erfüllen; Wie diese verschiedenen Typen diese Schnittstelle implementieren, ist orthogonal zum Polymorphismus. Der dynamische Polymorphismus sieht so aus:

%Vor%

Dabei ist d.algorithm eine Basisklasse, die eine virtuelle Base -Methode bereitstellt, die Derived , Base usw. überschreibt. Hier ist der statische Polymorphismus:

%Vor%

Und das ist es. Sie können die statische Version von Derived für jeden Typ aufrufen, der eine Derived -Methode definiert, ohne von einer Basisklasse zu erben. Und der Anruf wird zur Kompilierungszeit aufgelöst (d. H. Sie zahlen nicht für einen Vtable-Anruf).

Wo passt CRTP? Bei CRTP geht es wirklich nicht um die Schnittstelle, es geht also nicht um Polymorphie. Bei CRTP geht es darum, dass Sie Dinge einfacher implementieren können. Was CRTP magisch macht, ist, dass es Dinge direkt in die Schnittstelle eines Typs injizieren kann, mit vollem Wissen über alles, was der abgeleitete Typ bietet. Ein einfaches Beispiel könnte sein:

%Vor%

Nun kann jeder Klasse, die einen Additionsoperator definiert, auch eine Base -Methode zugewiesen werden:

%Vor%

Bei CRTP dreht sich alles um die Unterstützung bei der Implementierung, nicht um die Schnittstelle. Also nicht zu lange darüber auf, dass es oft als "statische Polymorphie" bezeichnet wird. Wenn Sie das echte kanonische Beispiel für die Verwendung von CRTP benötigen, sollten Sie Kapitel 1 von Andrei Alexandrescus Modern C ++ - Design in Betracht ziehen. Aber, nehmen Sie es langsam: -).

    
___ tag123c ___ C ++ ist eine universelle Programmiersprache. Es wurde ursprünglich als Erweiterung von C entworfen und behält eine ähnliche Syntax, ist aber jetzt eine komplett andere Sprache. Verwenden Sie dieses Tag für Fragen zu Code, der mit einem C ++ - Compiler kompiliert werden soll. ___ antwort43821627 ___

Die Vorteile von CRTP werden erst deutlich, wenn mehr als eine Funktion involviert ist. Betrachten Sie diesen Code (ohne CRTP):

%Vor%

Dies gibt aus:

  

0

[Live-Beispiel]

Aufgrund der statischen Natur des C ++ - Typsystems ruft der Aufruf von static_sub_func alle Funktionen von Derived::static_sub_func auf. Die versuchten Überschreibungen in virtual werden nicht aufgerufen.

Dies ändert sich, wenn CRTP verwendet wird:

%Vor%

Ausgabe:

  

Wir sind fertig!
  -26

[Live-Beispiel]

Auf diese Weise wird die Implementierung in Derived tatsächlich in %code% aufgerufen, wenn %code% eine "Überschreibung" bereitstellt.

Dies wäre sogar in Ihrem ursprünglichen Code sichtbar: Wenn %code% keine CRTP-Klasse ist, wird der Aufruf von %code% niemals in %code% aufgelöst.

Was die Vorteile von CRTP gegenüber anderen Ansätzen sind:

  • CRTP versus %code% Funktionen:

    CRTP ist ein Kompilierzeitkonstrukt, dh es ist kein Laufzeitaufwand verbunden. Das Aufrufen einer virtuellen Funktion über eine Basisklassenreferenz (normalerweise) erfordert einen Aufruf über einen Zeiger zur Funktion und somit indirekte Kosten und verhindert das Inlining.

  • CRTP versus einfach alles in %code% implementieren:

    Wiederverwendung der Basisklassencodes.

Natürlich ist CRTP ein rein kompilierbares Konstrukt. Um den kompilierbaren Polymorphismus zu erreichen, müssen Sie ein kompilierbares polymorphes Konstrukt verwenden: Templates. Es gibt zwei Möglichkeiten, dies zu tun:

%Vor%

Ersteres entspricht näher dem Laufzeit-Polymorphismus und bietet eine bessere Typensicherheit, letzteres ist mehr auf Duck-Typ basiert.

    
___ qstntxt ___

Ich versuche meinen Kopf um das CRTP zu wickeln. Es gibt einige gute Quellen, einschließlich dieses Forums, aber ich denke, ich habe etwas Verwirrung über die Grundlagen des statischen Polymorphismus. Blick auf den folgenden Wikipedia-Eintrag:

%Vor%

Ich verstehe, dass dies mir hilft, verschiedene implementation () - Varianten in abgeleiteten Klassen zu haben, ähnlich einer virtuellen Kompilierungsfunktion. Meine Verwirrung ist jedoch, dass ich glaube, dass ich keine Funktionen wie

haben kann %Vor%

wie bei normaler Vererbung und virtuellen Funktionen, da Base als Template verwendet wird, aber ich müsste

angeben %Vor%

oder verwenden

%Vor%

Also, was kauft mir CRTP in diesem Zusammenhang, anstatt einfach die Methode in Derived :: Base zu überschatten / zu implementieren?

%Vor%     
___ tag123crtp ___ Das seltsam wiederkehrende Vorlagenmuster (CRTP) ist ein C ++ - Idiom, in dem eine Klasse X von einer Klassenvorlageninstanziierung abgeleitet wird, wobei X selbst als Vorlagenargument verwendet wird. ___ answer44187495 ___

Sie haben Recht, dass weder

%Vor%

oder

%Vor%

gibt Ihnen statischen Polymorphismus. Die erste kompiliert nicht, weil %code% kein Typ und die zweite nicht polymorph ist.

Angenommen, Sie haben zwei abgeleitete Klassen, %code% und %code% . Dann können Sie %code% selbst zu einer Vorlage machen.

%Vor%

Dies kann dann mit jedem Typ aufgerufen werden, der von %code% abgeleitet ist, und es wird der statische Typ des übergebenen Parameters verwendet, um zu entscheiden, welche Funktion aufgerufen werden soll.

Dies ist nur eine der Anwendungen von CRTP, und wenn ich raten würde, würde ich das weniger häufige sagen. Sie können es auch als Nir Friedman in einer anderen Antwort verwenden, die nichts mit statischem Polymorphismus zu tun hat. p>

Beide Anwendungen werden sehr gut diskutiert hier

    
___
Angew 06.05.2017 14:28
quelle
0

Sie haben Recht, dass weder

%Vor%

oder

%Vor%

gibt Ihnen statischen Polymorphismus. Die erste kompiliert nicht, weil Base kein Typ und die zweite nicht polymorph ist.

Angenommen, Sie haben zwei abgeleitete Klassen, Derived1 und Derived2 . Dann können Sie func selbst zu einer Vorlage machen.

%Vor%

Dies kann dann mit jedem Typ aufgerufen werden, der von Base abgeleitet ist, und es wird der statische Typ des übergebenen Parameters verwendet, um zu entscheiden, welche Funktion aufgerufen werden soll.

Dies ist nur eine der Anwendungen von CRTP, und wenn ich raten würde, würde ich das weniger häufige sagen. Sie können es auch als Nir Friedman in einer anderen Antwort verwenden, die nichts mit statischem Polymorphismus zu tun hat. p>

Beide Anwendungen werden sehr gut diskutiert hier

    
Daniel H 25.05.2017 18:30
quelle

Tags und Links