2010-06-09 1 views
15

Ich versuche Code zu schreiben, der ein Member typedef eines Template-Arguments verwendet, aber einen Standardtyp angeben möchte, wenn das Template-Argument diesen Typdef nicht hat. Ein vereinfachtes Beispiel habe ich versucht, ist dies:Template-Spezialisierung zur Verwendung des Standardtyps, wenn das Klassenelement typedef nicht existiert

struct DefaultType { DefaultType() { printf("Default "); } }; 
struct NonDefaultType { NonDefaultType() { printf("NonDefault "); } }; 

struct A {}; 
struct B { typedef NonDefaultType Type; }; 

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
}; 
template<typename T> struct Get_Type< T, typename T::Type > { 
    typedef typename T::Type Type; 
}; 

int main() 
{ 
    Get_Type<A>::Type test1; 
    Get_Type<B>::Type test2; 
} 

Ich würde erwarten, dass dieses „Default Nicht-Standard“ drucken, sondern druckt „Default“. Meine Erwartung ist, dass die zweite Zeile in main() mit der spezialisierten Version von Get_Type übereinstimmen sollte, weil B :: Type existiert. Dies geschieht jedoch nicht.

Kann mir jemand erklären, was hier vor sich geht und wie man es repariert, oder einen anderen Weg, um dasselbe Ziel zu erreichen?

Vielen Dank.

Edit:

Georg gab eine alternative Methode, aber ich bin immer noch neugierig, warum dies nicht funktioniert. die den Auftrieb enable_if docs nach, eine Möglichkeit, eine Vorlage für verschiedene Typen spezialisiert ist etwa so:

template <class T, class Enable = void> 
class A { ... }; 

template <class T> 
class A<T, typename enable_if<is_integral<T> >::type> { ... }; 

template <class T> 
class A<T, typename enable_if<is_float<T> >::type> { ... }; 

Dies funktioniert, weil enable_if < true> als typedef hat geben, aber enable_if < false> nicht.

Ich verstehe nicht, wie dies anders ist als meine Version, wo anstelle von enable_if verwende ich nur T :: Type direkt. Wenn T :: Type existiert, wäre das nicht dasselbe wie enable_if < true> :: type im obigen Beispiel und die Spezialisierung wird gewählt? Und wenn T :: Type nicht existiert, wäre das nicht dasselbe wie enable_if < false> :: type not exists und verursacht die Standardversion im obigen Beispiel gewählt?

+0

Uh verwenden .. was ist das Ziel? –

+0

Das Ziel ist, dass Get_Type :: Type wird T :: Type, wenn es existiert, oder DefaultType, wenn es nicht existiert. – Frank

Antwort

8

Um Ihren Zusatz zu beantworten - Ihr Spezialisierungsargument übergibt das Mitglied typedef und erwartet, dass es void als Typ ergibt. Es gibt nichts Magisches daran - es verwendet nur ein Standardargument. Mal sehen, wie es funktioniert. Wenn Sie Get_Type<Foo>::type sagen, verwendet der Compiler das Standardargument Enable, das void ist, und der Typname wird Get_Type<Foo, void>::type. Jetzt prüft der Compiler, ob eine Teilspezialisierung übereinstimmt.

Die Argumentliste Ihrer Teilspezialisierung <T, typename T::Type> wird von der ursprünglichen Argumentliste <Foo, void> abgeleitet. Dies leitet T zu Foo und später ersetzt diese Foo in das zweite Argument der Spezialisierung, was ein Endergebnis von <Foo, NonDefaultType> für Ihre teilweise Spezialisierung ergibt. Dies entspricht jedoch nicht der ursprünglichen Argumentliste <Foo, void> überhaupt!

Sie benötigen einen Weg, um die void Typ zu ergeben, wie in der folgenden:

template<typename T> 
struct tovoid { typedef void type; }; 

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
}; 
template<typename T> 
struct Get_Type< T, typename tovoid<typename T::Type>::type > { 
    typedef typename T::Type Type; 
}; 

Jetzt wird dies funktionieren wie Sie es erwarten. Mit MPL, können Sie always statt tovoid

typename apply< always<void>, typename T::type >::type 
+0

Danke Johannes. Ich wusste nicht, dass der Typ selbst ungültig sein musste, um zu passen, aber das macht durchaus Sinn. – Frank

7

können Sie tun das, SFINAE durch Nutzung:

template<class T> struct has_type { 
    template<class U> static char (&test(typename U::Type const*))[1]; 
    template<class U> static char (&test(...))[2]; 
    static const bool value = (sizeof(test<T>(0)) == 1); 
}; 

template<class T, bool has = has_type<T>::value> struct Get_Type { 
    typedef DefaultType Type; 
}; 

template<class T> struct Get_Type<T, true> { 
    typedef typename T::Type Type; 
}; 
+0

Danke George, das funktioniert. Ich habe meine Frage mit einer anderen Frage, warum meine Implementierung nicht funktioniert, bearbeitet, weil es scheint, als ob das selbe Prinzip, auf dem enable_if basiert, auf demselben basiert. enable_if hat eine typedef, aber enable_if nicht. Ich verstehe nicht, warum das nicht dasselbe ist wie ob T :: Type existiert oder nicht. – Frank

+0

@Frank: Johannes hat das schon in einer anderen Antwort geantwortet. –

4

Erster Schritt: aufhören mit "Type" und den mpl Standard "Typ" verwenden.


BOOST_MPL_HAS_XXX_DEF(Type) 

template < typename T > 
struct get_type { typedef typename T::Type type; }; 

template < typename T > 
struct calculate_type : boost::mpl::if_ 
< 
    has_Type<T> 
, get_type<T> 
, boost::mpl::identity<default_type> 
>::type {} 

typedef calculate_type<A>::type whatever; 

Wenn Sie „Typ“ anstelle von „Type“ in Ihrem Metafunktionen verwendet würden Sie nicht die Abholer „get_type“ erfordern es zu konvertieren und konnte nur T in diesem Fall zurück.

Verwandte Themen