2014-11-04 10 views
6

Ich habe einige Artikel über SFINAE gelesen, kann aber keine Lösung für meinen Fall finden. Hier ist, was ich tun möchte:Wie benutzt man SFINAE um zwei verschiedene Implementierungen derselben Methode zu erstellen

#include <type_traits> 

struct CByteArray {}; 
struct HLVariant { 
    HLVariant() {} 
    HLVariant(const HLVariant&) {} 
    HLVariant(const CByteArray&) {} 

    }; 

template <typename T> 
struct Serializer 
{ 
    static inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T& value) 
    { 
     static_assert(std::is_pod<T>::value, "Not a POD type"); 
     return CByteArray(); 
    } 

    static inline typename std::enable_if<!std::is_pod<T>::value, CByteArray>::type serialize(const T& value) 
    { 
     return Serializer<HLVariant>::serialize(HLVariant(value)); 
    } 
}; 

template <> 
struct Serializer<HLVariant> 
{ 
    static inline CByteArray serialize(const HLVariant& value) 
    { 
     return CByteArray(); 
    } 
}; 

int main() 
{ 
    int i = 0; 
    Serializer<int>::serialize(i); 
    Serializer<CByteArray>::serialize(CByteArray()); 
    Serializer<HLVariant>::serialize(HLVariant()); 

    return 0; 
} 

Aber natürlich, ich bin immer error C2039: 'type' : is not a member of 'std::enable_if<false,CByteArray>'

Wie zu erreichen, was ich will?

auch sein, wäre es möglich, die Serializer irgendwie neu zu organisieren, so dass die Template-Parameter implizit abgeleitet werden können - Serializer::serialize(i); statt Serializer<int>::serialize(i);?

+1

@Nawaz Abstimmung, um Ihre Antwort wiederherzustellen, da es viel bessere Alternative zu dem Problem vorschlagen. –

+0

@ Nawaz: gleich. Ich habe meinen Code bereits mit diesem Ansatz kompiliert und getestet. Hab auch eine Frage dazu. –

Antwort

2

Um zu verwenden, müssen Sie in einer Vorlage über die Bedingung sein. Eine Option ist ein Hilfselement Funktion Vorlage serialize_t

template <typename T> 
struct Serializer 
{ 
    template<bool is_pod> // template over condition 
    static typename std::enable_if< is_pod, CByteArray>::type serialize_t(const T& value) 
    { return CByteArray(); } 

    template<bool is_pod> 
    static typename std::enable_if<!is_pod, CByteArray>::type serialize_t(const T& value) 
    { return Serializer<HLVariant>::serialize(HLVariant(value)); } 

    static CByteArray serialize(const T& value) 
    { return serialize_t<std::is_pod<T>::value>(value); } 
}; 

template<> 
struct Serializer<HLVariant> 
{ 
    static CByteArray serialize(const HLVariant&) 
    { return CByteArray(); } 
}; 

Alternativ zu erklären, Sie SFINAE anwenden können direkt am Umfang der Klassenvorlage:

template<typename T, typename = void> struct Serializer; 

template<> 
struct Serializer<HLVariant> 
{ 
    static CByteArray serialize(const HLVariant&) 
    { return CByteArray(); } 
}; 

template<typename T> 
struct Serializer<T,typename std::enable_if<is_pod<T>::type> 
{ 
    static CByteArray serialize(const T&) 
    { return CByteArray(); } 
}; 

template<typename T> 
struct Serializer<T,typename std::enable_if<!is_pod<T>::type> 
{ 
    static CByteArray serialize(const T&value) 
    { return Serializer<HLVariant>::serialize(HLVariant(value)); 
}; 

Oder Sie könnten der Klasse Serializer loszuwerden und erklären dies direkt über Template-Funktionen:

inline CByteArray 
serialize(const HLVariant&) 
{ return CByteArray(); } 

template<typename T> 
inline typename enable_if<std::is_pod<T>::value, CByteArray>::type 
serialize(const T&) 
{ return CByteArray(); } 

template<typename T> 
inline typename enable_if<!std::is_pod<T>::value, CByteArray>::type 
serialize(const T&value) 
{ return serialize(HLVariant(value)); } 

BTW, C++ 14 die sehr nützlich alias definiert

template<bool C, typename T> 
using enable_if_t = typename enable_if<C,T>::type; 

aber Sie können das natürlich auch tun. Dies vermeidet die lästigen typename und ::type die ganze Zeit.

+0

Ihr letzter Vorschlag ist etwas, womit ich angefangen habe.Es funktioniert, wird aber schnell zu kompliziert, da ich immer wieder "Spezialisierungen" hinzufüge (für das Fehlen eines besseren Wortes; das sind natürlich keine Spezialisierungen in C++). –

+0

Ich fürchte, das Schreiben einer allgemeinen Funktionalität für einen allgemeinen Eingabetyp mit verschiedenen Implementierungen für verschiedene Arten von Eingabetyp wird kompliziert ... – Walter

+0

Nicht, wenn Spezialisierungen auf eine vernünftige Art und Weise funktionierte! Sieht so aus, als müssten wir wirklich eine Art "statisches if" dafür brauchen. –

3

SFINAE ist ein Akronym für "Substitutionsfehler ist kein Fehler." Per Definition bedeutet dies, dass es nur angewendet wird, wenn Template-Argumente für Parameter in der Definition einer Vorlage ersetzt werden. Ihre serialize Funktionen sind Mitgliederfunktionen einer Klassenvorlage, sie sind selbst keine Funktionsvorlagen. Die direkte Antwort wäre, die Funktionen in Funktionsschablonen zu konvertieren (Live code):

template <typename> struct Serializer; 

template <> 
struct Serializer<HLVariant> 
{ 
    static CByteArray serialize(const HLVariant& /* value */) 
    { 
     return CByteArray(); 
    } 
}; 

template <typename T> 
struct Serializer 
{ 
    template <typename U = T> 
    static typename std::enable_if<std::is_pod<U>::value, CByteArray>::type 
    serialize(const U& /* value*/) 
    { 
     static_assert(std::is_pod<U>::value, "Not a POD type"); 
     return CByteArray(); 
    } 

    template <typename U = T> 
    static typename std::enable_if<!std::is_pod<U>::value, CByteArray>::type 
    serialize(const U& value) 
    { 
     return Serializer<HLVariant>::serialize(HLVariant(value)); 
    } 
}; 

ich die redundanten inline s entfernt haben, da alle in einer Klasse Körper definierten Funktionen implizit inline sind, und ich zog die Serializer<HLVariant> Spezialisierung Stellen Sie sicher, dass es korrekt deklariert ist, bevor Sie darauf verwiesen werden. Es ist ein bisschen dumm, eine Klasse mit nur statischen Memberfunktionen zu haben; Sie könnten dies als eine Reihe von überladenen Funktionen (Live code) mehr vernünftig umzusetzen:

inline CByteArray serialize(const HLVariant& /* value */) 
{ 
    return CByteArray(); 
} 

template <typename T> 
inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type 
serialize(const T& /* value*/) 
{ 
    static_assert(std::is_pod<T>::value, "Not a POD type"); 
    return CByteArray(); 
} 

template <typename T> 
inline typename std::enable_if<!std::is_pod<T>::value, CByteArray>::type 
serialize(const T& value) 
{ 
    return serialize(HLVariant(value)); 
} 

int main() 
{ 
    int i = 0; 
    serialize(i); 
    serialize(CByteArray()); 
    serialize(HLVariant()); 
} 

Da SFINAE Verwendung erschwert die Lesbarkeit des Codes, ziehe ich würde Tag in diesem Fall den Versand zu verwenden. Statt Überlastung Auflösung von zwei Funktionen mit SFINAE der Verwaltung, eine dritte Funktion, die die entsprechende Implementierung für POD oder nicht-POD-Anrufe (Yet more live code):

inline CByteArray serialize(const HLVariant& /* value */) 
{ 
    return CByteArray(); 
} 

template <typename T> 
inline CByteArray serialize(std::true_type, const T& /* value*/) 
{ 
    static_assert(std::is_pod<T>::value, "Not a POD type"); 
    return CByteArray(); 
} 

template <typename T> 
inline CByteArray serialize(std::false_type, const T& value) 
{ 
    return serialize(HLVariant(value)); 
} 

template <typename T> 
inline CByteArray serialize(const T& value) 
{ 
    return serialize(std::is_pod<T>{}, value); 
} 

SFINAE ist mächtig, aber gefährlich genug sicher verriegelt zu werden links weg für Probleme, die Sie mit einfacheren Tools lösen können.

+0

Cooler Trick, der erste. Jetzt verstehe ich meinen Fehler. Ich habe anfänglich mit freien Template-Funktionen anstelle von Klassenvorlagen begonnen, aber es gibt einen Bug in MSVC, der Template-Funktion-Spezialisierungen in meinem Fall unbrauchbar macht: http://stackoverflow.com/questions/26716609/using-stdenable-if-on-template -function-return-type-toexploit-sfinae-compila –

+0

* SFINAE ist gefährlich genug, um sicher verschlossen zu bleiben * Was ist daran gefährlich? und wie werden diese Gefahren durch Tag-Dispatching vermieden? Ich stimme zu, dass SFINAE die Lesbarkeit von Code erschwert, aber das Tag-Dispatching ist nicht viel besser, es ist sogar gefährlich, da es keine Garantie dafür gibt, dass serialize (std :: false_type, const T & value) nicht mit dem falschen zweiten Argument aufgerufen wird (da es nicht "privat" oder "geschützt" ist – Walter

Verwandte Themen