5

Angenommen, wir haben die folgende Template-KlasseHilfe mit Typ-Traits

template<typename T> class Wrap { /* ... */ }; 

Wir nichtWrap ändern können. Es ist wichtig.

Es gibt Klassen, die von Wrap<T> abgeleitet sind. Zum Beispiel

class NewInt : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 
class Foo  : public Wrap<Bar>  { /* ... */ }; 

Wir nicht diese Klassen ändern. Alle oben genannten Klassen sind Drittanbieter. Das sind nicht meine.

Ich brauche die folgenden Kompilierung type_traits:

template<class T> 
struct is_derived_from_Wrap { 
    static const bool value = /* */; 
}; 

Was brauche ich?

assert(is_derived_from_Wrap<Int>::value == true); // Indeed I need static assert 
assert(is_derived_from_Wrap<MyClass>::value == true); 
assert(is_derived_from_Wrap<char>::value == false); 
struct X {}; 
assert(is_derived_from_Wrap<X>::value == false); 
+0

Aber können Sie 'Int' und' MyClass' ändern? : p – kennytm

+0

Nein. Vielen Dank für Ihren Hinweis. –

+2

Wäre nicht eine bessere Benennung für Ihre Typeigenschaften: 'has_Wrap_for_base'? Eigentlich ist MyClass keine Basis von Wrap. –

Antwort

9

Sie können dies mit SFINAE tun, aber seine Art magische wenn Sie nicht wissen, was los ist ...

template<typename T> class Wrap { }; 

struct myclass {}; 
struct X {}; 

class Int  : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 

template< typename X > 
struct is_derived_from_Wrap 
{ 
    struct true_type { char _[1]; }; 
    struct false_type { char _[2]; }; 

    template< typename U > 
    static true_type test_sfinae(Wrap<U> * w); 
    static false_type test_sfinae(...); 

    enum { value = sizeof(test_sfinae((X*)(0)))==sizeof(true_type) }; 
}; 


#include <iostream> 
#define test(X,Y) std::cout<<(#X " == " #Y)<<" : "<<((X)?"true":"false") <<std::endl; 

int main() 
{ 
    test(is_derived_from_Wrap <Int>::value, true); 
    test(is_derived_from_Wrap <MyClass>::value, true); 
    test(is_derived_from_Wrap <char>::value, false); 
    test(is_derived_from_Wrap <X>::value, false); 
} 

Das die erwartete Ausgabe

is_derived_from_Wrap <Int>::value == true : true 
is_derived_from_Wrap <MyClass>::value == true : true 
is_derived_from_Wrap <char>::value == false : false 
is_derived_from_Wrap <X>::value == false : false 

Es gibt ein paar Fallstricke gibt mit meinem Code. Es wird auch true zurückgegeben, wenn der Typ ein Umbruch ist.

Dies kann wahrscheinlich mit etwas mehr SFINAE Magie behoben werden, wenn nötig.

Es wird falsch zurück, wenn die Ableitung noch keine öffentliche Ableitung (das heißt ist privat oder geschützt)

struct Evil : private Wrap<T> { }; 
assert(is_derived_from_Wrap<Evil>::value == 0); 

Ich vermute, das nicht behoben werden kann. (Aber ich kann falsch liegen). Aber ich vermute, öffentliche Vererbung ist genug.

+0

Ich nehme dann an, dass einfache Teilspezialisierung wie in meinem Code nicht funktioniert? – sbi

+0

Korrigieren Sie, da die Spezialisierung nur dann übereinstimmt, wenn der Typ genau übereinstimmt. Ihr is_Wrap < Wrap> :: value = true, aber is_Wrap :: value = false. –

+0

Erstaunlich. Ich dachte, dass 'test_sfinae (Wrap * w);' 'U' für den Fall von' X * 'nicht abgeleitet werden kann. –

0

Im Folgenden legt fest, ob etwas ist ein Wrap:

template<class T> 
struct is_Wrap { static const bool value = false; }; 

template<typename T> 
struct is_Wrap< Wrap<T> > { static const bool value = true; }; 

Da Ableitung ist eine Is-A-Beziehung, alles abgeleitet von Wrap<T> ist auch ein Wrap<T> und sollte durch diese gefunden werden.

+0

Mein Name 'Base of Wrap' ist falsch. Mein schlechtes Englisch. 'Abgeleitet von Wrap', natürlich. –

+0

Es ist keine sehr gute Lösung. Dann muss ich DEFINE für jede Klasse machen. Ich kenne sie nicht alle. Ich möchte erkennen, ob X von Wrap abgeleitet ist, wobei Y ein beliebiger Typ sein kann. –

+0

@Alexey: Ich werde versuchen, meinen Code entsprechend anzupassen. Aber Sie müssen auf jeden Fall präziser sein, wenn Sie Ihre Fragen stellen. (Und das ist nicht nur ein Sprachbarriere-Problem.) – sbi

0

Sie müssen einige ziemlich involvierte Template-Metaprogrammierung durchführen, um zu bestimmen, ob eine Klasse X von einem anderen Y abgeleitet ist, im allgemeinen Fall. Grundsätzlich X leitet sich von Y, wenn:

  1. Y implizit in X umgewandelt werden
  2. X und Y sind nicht vom gleichen Typ

Andrei Alexandrescu erklärt, wie dies zu tun (zusammen mit vielen anderen Template-Tricks) in seinem Buch "Modern C++ Design".

Sie können Code finden, der Ihr Problem entweder in der Loki Bibliothek oder der uSTL Implementierung löst, beide von Alexandrescu geschrieben.

+1

Nachdem ich die Frage erneut gelesen habe, merke ich, dass mein Vorschlag das Problem nicht löst. Wir wollen nicht wissen, ob X von Y abgeleitet ist, sondern ob X von Y stammt, wobei T ein beliebiger Typ sein kann. –

+0

Ja. Ihre Antwort ist keine Lösung. Aber Sie haben das Problem erkannt. –

+0

Sie könnten auch boost :: is_base_of angeben (das das Problem des OPs nicht anspricht, da der Base-Parameter keine Klassenvorlage sein kann). –