2015-07-10 2 views
10

Der folgende Code kompiliert und gibt Ergebnis als in (GCC und Klappern) erwarten:C++ dynamic_cast zum Weiterleiten deklarierter Klassenvorlagen kompiliert, aber ist es sicher?

template <typename T> struct Derived; 

struct Base 
{ 
    template <typename T> 
    void foo(T * const t) 
    { 
     dynamic_cast<Derived<T> * const>(this)->bar(t); 
    } 
}; 

template <typename T> 
struct Derived : Base 
{ 
    void bar(T const *) const { } 
}; 

Der Code einen Aufruf an foo in Base-bar in Derived entsendet.

Als Bezugspunkt, wird der folgende Code nicht kompiliert:

main.cpp: In member function 'void Base2::foo(T*)': 
main.cpp:126:45: error: invalid use of incomplete type 'struct Derived2' 
     dynamic_cast<Derived2 * const>(this)->bar(t); 
              ^
main.cpp:119:8: note: forward declaration of 'struct Derived2' 
struct Derived2; 
     ^

Die C++ 14-Standard ist in dem Abschnitt über die One:

struct Derived2; 

struct Base2 
{ 
    template <typename T> 
    void foo(T * const t) 
    { 
     dynamic_cast<Derived2 * const>(this)->bar(t); 
    } 
}; 

struct Derived2 : Base2 
{ 
    template <typename T> 
    void bar(T const *) const { } 
}; 

GCC folgende Diagnose liefert Definitionsregel, folgende:

5 In einer Übersetzungseinheitist genau eine Definition einer Klasse erforderlichWenn die Klasse so verwendet wird, dass der Klassentyp vollständig sein muss.
[Beispiel: die folgende vollständige Übersetzungseinheit ist wohlgeformt, obwohl sie niemals X definiert:
struct X; // deklariere X als Strukturtyp
struct X * x1; // verwende X in der Zeigerformation
X * x2; // verwende X bei der Zeigerbildung
-Ende Beispiel]
[Hinweis: Die Regeln für Deklarationen und Ausdrücke beschreiben, in welchen Kontexten vollständige Klasse Typen erforderlich sind. Eine Klassenart T muss vollständig sein, wenn: (5.1) - ein Objekt vom Typ T definiert ist (3.1), oder
(5.2) - ein nicht statisches Klassendatenelement des Typs T wird deklariert (9.2) oder
(5.3) - T wird als Objekttyp oder Arrayelementtyp in einem neuen Ausdruck (5.3.4) oder
(5.4) verwendet - eine lvalue-to-rvalue-Konvertierung wird auf einen glvalue bezogen auf ein Objekt angewendet vom Typ T (4.1) oder
(5.5) - ein Ausdruck wird (entweder implizit oder explizit) in Typ T (Abschnitt 4, 5.2.3, 5.2.7, 5.2.9, 5.4) oder
(5.6) - Ein Ausdruck, der keine Null-Zeigerkonstante ist und einen anderen Typ als cv void * hat, wird unter Verwendung einer Standardkonvertierung in den Typzeiger nach T oder Verweis auf T umgewandelt. Abschnitt 4), ein dynamic_cast (5.2.7) oder ein statischer_cast (5.2.9) oder ...

Dies scheint zu sagen, dass das erste Beispiel nicht legal ist. Das ist ein Konstrukt schlecht geformt? Wenn ja, warum bekomme ich keinen Fehler?

+6

Bei abhängigen Namen wird die Vollständigkeit zum Zeitpunkt der Instanziierung gemessen. –

+2

Damit dynamic_cast ordnungsgemäß funktioniert, benötigen Sie Polymorphismus, d. H. Mindestens eine virtuelle Funktion. Da Sie das Ergebnis nicht einmal überprüfen, verwenden Sie lieber das Referenzformular, in dem C++ eine Ausnahme bei einem Fehler auslöst. –

+0

Das klingt wie ein klassisches Beispiel für [Zwei-Phasen-Lookup] (http://stackoverflow.com/questions/7767626/two-phase-lookup-explanation-needed). – ComicSansMS

Antwort

2

EDIT: Nach ein wenig Nachdenken: Vorlagen werden zuerst definiert, wenn Sie sie instanziieren. Ihr erster Code funktioniert also, weil die Vorlage zuerst definiert wird, wenn der Compiler die Zeile erreicht hat, in der Sie die Vorlagenklasse instanziieren.

Verwandte Themen