2014-11-18 4 views
7

Ich habe einen interessanten Fall, in dem versucht wird, innerhalb einer Template-Funktion zu verzweigen, wobei der Pfad von der vom Template-Typ implementierten Schnittstelle abhängt. Dieser Zweig bestimmt dann den Konstruktor des Rückgabewerts. Ich bin mir nicht sicher, ob diese Art der Verzweigung möglich ist. Die Alternative besteht darin, die Funktion in zwei verschiedene Funktionen aufzuteilen und den Benutzer denjenigen aufrufen zu lassen, der dem gewünschten Zweig entspricht.C++ - Template-Verzweigung

Meine Frage ist zweifach:

Wie kann ich die if-Anweisung auf der Grundlage der Schnittstellen-Implementierung durchführen?

Wie kann ich die Funktion kompilieren, wenn die Schnittstelle nicht implementiert ist? Zum Beispiel hat int keinen Konstruktor mit zwei Parametern.

template<typename T> 
T GetObject() 
{ 
    // If T implements interface, call interface constructor 
    if(typeid(T) implements inteface IInterface) // How can this be done? 
     return T(1, 2); 

    // If T does not implement the interface, call parameterless constructor 
    else 
     return T(); 
} 
+0

Was versuchst du zu erreichen? Ihr Vorlagenbeispiel ist unmöglich, aber es kann einen einfachen Weg geben, Ihr tatsächliches Ziel zu erreichen. – patros

+0

Dies ist Teil eines Frameworks innerhalb meiner Software für die Serialisierung. Dies ist der Punkt, an dem entweder das Rückgabeobjekt basierend auf einigen gespeicherten Bytes zugewiesen wird oder das zurückgegebene Objekt über untergeordnete Objekte verfügt, die abgerufen werden müssen. Die Schnittstelle in meiner Frage ist die ISerializable-Schnittstelle, die ich erstellt habe. Barry hat mir genau das gebracht, was ich brauchte. Ich habe diese Frage gestellt, um zu versuchen, meine 'T GetObject()' und 'ISerializable GetSerializedObject()' Funktionen zu kombinieren. –

Antwort

9

Wie man unschwer ableiten kann, Ihr Code hat, wie es keine Möglichkeit, zu arbeiten (obwohl ich wünschte, es tut), weil unabhängig davon, was Sie setzen in Ihrer if-Anweisungen, hat jede Zeile erstellt werden - auch wenn Einige werden später während der Optimierung entfernt. Selbst wenn Sie die richtige Linie für haben:

if(typeid(T) implements inteface IInterface) 
    return T(1,2); // <--- 

Die zweite Zeile noch für int kompiliert werden würde, was nicht gut ist.

Was Sie wollen, ist SFINAE: Substitutionsfehler ist kein Fehler. Im Grunde schreiben Sie absichtlich eine Deklaration, die nicht kompilieren kann, aber solange es eine verfügbare Überladung gibt, die kompiliert wird, das ist gut genug! So zu diesem Zweck nehmen wir nur den Vorteil von einigen Art Züge und die Magie, aber unglaublich einfach, enable_if:

// this overload will only exist if T is-a IInterface 
template<typename T> 
typename std::enable_if< 
    std::is_base_of<IInterface, T>::value, 
    T 
>::type 
GetObject() { 
    return T(1,2); 
} 

// this overload will only exist if T is NOT an IInterface 
template<typename T> 
typename std::enable_if< 
    !std::is_base_of<IInterface, T>::value, 
    T 
>::type 
GetObject() { 
    return -1; 
} 

// explicit for int - put this *last* 
// due to complications with specialization 
template <> 
int GetObject() { 
    return 0; 
} 

Obwohl Herb Sutter gegeben article auf Funktion Spezialisierung, wahrscheinlich besser Überlastung zu verwenden:

template <typename T> struct empty_ { }; 

template <typename T> 
T GetObject() { return GetObjectImpl(empty_<T>{}); } 

// the int version: overload, not specialization 
int GetObjectImpl(empty_<int>) { 
    return 0; 
} 

// the other two versions are the same as before 
template <typename T> 
typename std::enable_if<... same as before ...>::type 
GetObjectImpl(empty_<T>) { 
    return T(1,2); 
} 
+1

Schöne Antwort. Eine Minute Massage in mein Projekt und deine Antwort hat perfekt funktioniert. Danke für die Beantwortung beider Fragen und schnell. –