2009-07-10 15 views
7

Wir haben ein Unterprojekt 'commonUtils', das viele generische Code-Snippets im übergeordneten Projekt verwendet. i Eine solche interessante Sachen sah, war: -Testen Sie, ob eine Klasse polymorph ist

/********************************************************************* 
If T is polymorphic, the compiler is required to evaluate the typeid 
stuff at runtime, and answer will be true. If T is non-polymorphic, 
the compiler is required to evaluate the typeid stuff at compile time, 
whence answer will remain false 
*********************************************************************/ 
template <class T> 
bool isPolymorphic() { 
    bool answer=false; 
    typeid(answer=true,T()); 
    return answer; 
} 

ich den Kommentar geglaubt und dachte, dass es eine sehr interessante Vorlage ist, obwohl es nicht über das Projekt verwendet wird. Ich habe versucht, es nur aus Neugier so zu verwenden ...

class PolyBase { 
public: 
    virtual ~PBase(){} 
}; 

class NPloyBase { 
public: 
    ~NBase(){} 
}; 


if (isPolymorphic<PolyBase>()) 
    std::cout<<"PBase = Polymorphic\n"; 
if (isPolymorphic<NPolyBase>()) 
    std::cout<<"NBase = Also Polymorphic\n"; 

Aber keines von denen kehrt jemals zurück. MSVC 2005 gibt keine Warnungen, aber Comeau warnt, dass typeid Ausdruck keine Auswirkung hat. Abschnitt 5.2.8 im C++ - Standard sagt nichts darüber aus, was der Kommentar sagt, d. H. Typid is wird zur Kompilierungszeit für nicht-polymorphe Typen und zur Laufzeit für polymorphe Typen ausgewertet.

1) Also ich denke, der Kommentar ist irreführend/plain-falsch oder da der Autor dieses Codes ist ziemlich ein Senior C++ - Programmierer, fehlt mir etwas?

2) OTOH, ich frage mich, ob wir testen können, ob eine Klasse polymorph ist (hat mindestens eine virtuelle Funktion) mit einer Technik?

3) Wann möchte man wissen, ob eine Klasse polymorph ist? Wilde Vermutung; Um die Startadresse einer Klasse zu erhalten, verwenden Sie dynamic_cast<void*>(T) (wie dynamic_cast funktioniert nur auf polymorphe Klassen).

In Erwartung Ihrer Meinungen.

Vielen Dank im Voraus,

+0

Er, Wenn der Autor ein älterer C++ - Programmierer ist, warum überprüfen Sie nicht zuerst mit ihm? ... Sie werden oft viel von erfahrenen Leuten lernen. – stefanB

+9

Nun, wenn ich könnte ich hätte es nicht auf stackoverflow gefragt :-) – Abhay

Antwort

8

ich keine Möglichkeit, sich vorstellen kann, wie das typeid verwendet werden könnte, diese Art zu überprüfen, ist polymorph. Es kann nicht einmal verwendet werden, um dies zu bestätigen, da typeid auf jedem Typ funktioniert. Boost hat eine Implementierung here. Warum sollte es notwendig sein? Ein Fall, den ich kenne, ist die Boost.Serialization-Bibliothek. Wenn Sie einen nicht-polymorphen Typ speichern, können Sie ihn einfach speichern. Wenn Sie eine polymorphe Version speichern, müssen Sie den dynamischen Typ mit typeid abrufen und dann die Serialisierungsmethode für diesen Typ aufrufen (in einer Tabelle nachschlagen).

Update: es scheint, ich bin eigentlich falsch. Betrachte diese Variante:

Dies funktioniert tatsächlich wie der Name schon sagt, genau nach Kommentar in Ihrem ursprünglichen Code-Snippet. Der Ausdruck innerhalb von typeid wird nicht ausgewertet, wenn er "keinen Wert des polymorphen Klassentyps angibt" (Standard 3.2/2). Wenn im obigen Fall T nicht polymorph ist, wird der Typid-Ausdruck nicht ausgewertet. Wenn T polymorph ist, dann ist * t tatsächlich ein Wert des polymorphen Typs, so dass der gesamte Ausdruck ausgewertet werden muss.

Jetzt ist Ihr ursprüngliches Beispiel immer noch falsch :-). Es verwendete T(), nicht *t. Und T() erstellen rvalue (Std 3.10/6). Es ergibt also immer noch einen Ausdruck, der nicht "Wert der polymorphen Klasse" ist.

Das ist ziemlich interessanter Trick. Auf der anderen Seite ist sein praktischer Wert etwas begrenzt - denn während boost :: is_polymorphic Ihnen eine Kompilierzeitkonstante gibt, gibt Ihnen diese einen Laufzeitwert, so dass Sie keinen unterschiedlichen Code für polymorphe und nicht-polymorphe Typen instanziieren können .

+0

Ja, ich kenne die Boost-Implementierung, die grob die sizeof() - Technik verwendet. Danke für den Serialisierungs-Leckerbissen. Ich war daran interessiert zu wissen, ob diese commonUtils-Vorlage erstens richtig ist und zweitens ist es wert, dass sie in dem Projekt erhalten bleibt. – Abhay

+2

Ah ha, ein interessanter Trick in der Tat, aber Ihr Zitat der 3.10/6 war aufschlussreich, Danke. Man kann über die templated-Version wählen, wenn es um Binärgröße geht, oder wenn dem Benutzer nicht vertraut werden soll, ein virtuelles dtor in einer polymorphen Klasse bereitzustellen. Leider kann ich nicht 2 mal upvote! – Abhay

3


class PolyBase { 
public: 
    virtual ~PolyBase(){} 
}; 

class NPolyBase { 
public: 
    ~NPolyBase(){} 
}; 

template<class T> 
struct IsPolymorphic 
{ 
    struct Derived : T { 
     virtual ~Derived(); 
    }; 
    enum { value = sizeof(Derived)==sizeof(T) }; 
}; 


void ff() 
{ 
    std::cout << IsPolymorphic<PolyBase >::value << std::endl; 
    std::cout << IsPolymorphic<NPolyBase>::value << std::endl; 
} 

+1

Ich bin mir nicht sicher, ob dies völlig narrensicher ist. Ein Compiler kann Zwischenräume zwischen Unterobjekten hinzufügen, in welchem ​​Fall sizeof() trick nicht funktionieren würde. – Abhay

+1

Dies würde brechen, wenn das Abgeleitete seine eigenen Member-Variablen definiert, was es unpraktisch macht. – Indy9000

+0

@Indeera: Sie würden keine Elementvariablen hinzufügen, da die abgeleitete struct Abgeleitet absichtlich öffentlich von der von Ihnen angegebenen Klasse abgeleitet wird. Die einzige Einschränkung besteht darin, dass die angegebene Klasse kein virtuelles dtor, sondern einige virtuelle Funktionen (in diesem Fall ist die angegebene Klasse immer noch polymorph) außer dem Padding-Problem aufweist. Boost geht davon aus, dass eine polymorphe Klasse ein virtuelles dtor definiert und das Padding mit einigen Compiler-Details behandelt, denke ich. – Abhay

-1

Ich bin ein wenig verwirrt hier und hoffe, einige Kommentare zu dieser Antwort zu bekommen, die erklären, was ich vermisse. Wenn Sie herausfinden wollen, ob eine Klasse polymorph ist, müssen Sie nur fragen, ob sie dynamic_cast unterstützt, oder?

template<class T, class> struct is_polymorphic_impl : false_type {}; 
template<class T> struct is_polymorphic_impl 
    <T, decltype(dynamic_cast<void*>(declval<T*>()))> : true_type {}; 

template<class T> struct is_polymorphic : 
    is_polymorphic_impl<remove_cv_t<T>, void*> {}; 

Kann jemand einen Fehler in dieser Implementierung hinweisen? Ich stelle mir vor, dass es einen geben muss oder irgendwann einmal in der Vergangenheit gewesen sein muss, weil the Boost documentation weiterhin behauptet, dass is_polymorphic "nicht portabel in der C++ Sprache implementiert werden kann".

Aber "portabel" ist irgendwie ein Wieselwort, oder? Vielleicht verweisen sie nur darauf, wie MSVC Expression-SFINAE nicht unterstützt, oder einige Dialekte wie Embedded C++ unterstützen dynamic_cast nicht. Wenn sie "die C++ - Sprache" sagen, meinen sie vielleicht "eine Teilmenge der C++ - Sprache mit dem kleinsten gemeinsamen Nenner". Aber ich habe einen nagenden Verdacht, dass sie vielleicht meinen, was sie sagen, und Ich bin derjenige, der etwas vermisst.

Der typeid Ansatz im OP (wie durch eine spätere Antwort geändert einen L-Wert nicht ein R-Wert verwenden) scheint auch in Ordnung, aber natürlich ist es nicht constexpr und es erfordert die Konstruktion tatsächlich ein T, die super teuer sein könnte. So scheint diese dynamic_cast Ansatz besser ... es sei denn, es funktioniert nicht aus irgendeinem Grund. Gedanken?

Verwandte Themen