2013-04-09 6 views
36

Ich möchte eine Funktionsvorlage spezialisieren, so dass der Rückgabetyp abhängig vom Typ des Vorlagearguments ändert.Überschreiben Rückgabetyp in Funktionsvorlage Spezialisierung

class ReturnTypeSpecialization 
{ 
public: 
    template<typename T> 
    T Item(); 
}; 

// Normally just return the template type 
template<typename T> 
T ReturnTypeSpecialization::Item() { ... } 

// When a float is specified, return an int 
// This doesn't work: 
template<float> 
int ReturnTypeSpecialization::Item() { ... } 

Ist das möglich? Ich kann C++ 11 nicht verwenden.

+0

Was versuchen Sie zu erreichen? – didierc

+0

Ich möchte, dass eine Funktion den Typ zurückgibt, der als Vorlageargument bereitgestellt wird, außer in einem speziellen Fall möchte ich, dass die Funktion einen anderen Typ zurückgibt. –

+0

Nur für den Datensatz: Wenn das Template-Argument vom Typ eines Arguments abgeleitet und nicht explizit angegeben wird, ist es einfacher, einen anderen Typ zurückzugeben, indem Sie eine Funktionsüberladung verwenden. (Natürlich wird das in diesem Beispiel nicht funktionieren, da es keine Argumente gibt) – jorgbrown

Antwort

38

Da die Spezialisierung hat mit der Basisvorlage auf dem Rückgabetyp zu vereinbaren, können Sie es durch Hinzufügen eines „Rückgabetyp Merkmal“, eine Struktur zu machen, so können Sie den wahren Rückgabetyp spezialisiert und zeichnen aus:

// in the normal case, just the identity 
template<class T> 
struct item_return{ typedef T type; }; 

template<class T> 
typename item_return<T>::type item(); 

template<> 
struct item_return<float>{ typedef int type; }; 
template<> 
int item<float>(); 

Live example.

Beachten Sie, dass Sie auf die folgenden bleiben möchten, so dass Sie nur die Rückkehr-Typ in der item_return Spezialisierung aktualisieren müssen.

template<> 
item_return<float>::type foo<float>(){ ... } 
// note: No `typename` needed, because `float` is not a dependent type 
+1

+1 Ich mag dieses: P – Rapptz

+2

Stellen Sie sicher, dass alle Spezialisierungen deklariert werden, bevor sie instanziiert werden können. Praktisch bedeutet dies, dass die Spezialisierung in derselben Header-Datei deklariert werden muss und die Deklarationen möglichst nahe beieinander liegen sollten. – aschepler

+0

@aschepler: Sehr guter Punkt. Mit Spezialisierungen in dieser Form herumzuspielen ist ziemlich volatil. – Xeo

4

Sie können wie so Template Spezialisierungen tun:

template<typename T> 
T item() { 
    return T(); 
} 

template<> 
float item<float>() { 
    return 1.0f; 
} 
+2

Das würde bedeuten, dass 'T' auch immer der Rückgabetyp ist, was aus dem Beispiel in der Frage nicht der Fall zu sein scheint. – Xeo

+0

@Zeta: Sie können sie vollständig spezialisieren (auch * explizite Spezialisierung * genannt). – Xeo

+0

@Xeo: Titel der Frage: * " Überschreibender Rückgabetyp in der Funktionsvorlagenspezialisierung " *. Wahrscheinlich ein Tippfehler im Inhalt der Frage. – Zeta

4

Vielleicht könnten Sie den folgenden Hack verwenden. Aufgrund dieser einfachen Art Merkmale:

template<bool b, typename T, typename U> 
struct conditional { typedef T type; }; 

template<typename T, typename U> 
struct conditional<false, T, U> { typedef U type; }; 

template<typename T, typename U> 
struct is_same { static const bool value = false; }; 

template<typename T> 
struct is_same<T, T> { static const bool value = true; }; 

Sie können Ihre Klasse und Fachelementfunktion wie folgt schreiben:

class ReturnTypeSpecialization 
{ 
public: 
    template<typename T> 
    typename conditional<is_same<T, float>::value, int, T>::type 
    Item(); 
}; 

// Normally just return the template type 
template<typename T> 
typename conditional<is_same<T, float>::value, int, T>::type 
ReturnTypeSpecialization::Item() { return T(); } 

// When a float is specified, return an int 
template<> 
int ReturnTypeSpecialization::Item<float>() { return 1.0f; } 

Einfache Testprogramm (verwendet C++ 11 nur für Überprüfung):

int main() 
{ 
    ReturnTypeSpecialization obj; 
    static_assert(std::is_same<decltype(obj.Item<bool>()), bool>::value, "!"); 
    static_assert(std::is_same<decltype(obj.Item<float>()), int>::value, "!"); 
} 

Hier ist ein live example.

+0

Er hat kein C++ 11. – Rapptz

+0

@Rapptz: OK, tut mir leid, das habe ich verpasst. Aber ich glaube, dass dies ohne C++ 11-Feature neu geschrieben werden könnte ("std :: conditional" ist nicht schwer zu schreiben) –

+0

Ich denke, du hast deine Kondition falsch herum. Auch das ist schön, um mehr Spezialisierungen zu erweitern und noch schlimmer, alle Spezialisierungen müssen vorher bekannt sein. – Xeo

2

Führen Sie alle Spezialisierungen in einer Worker-Klasse durch und verwenden Sie eine einfache Funktion als Wrapper, der implizit spezialisiert wird.

#include <iostream> 
using std::cout; 

// worker class -- return a reference to the given value 
template< typename V > struct worker 
    { 
    typedef V const & type; 
    static type get(V const & v) { return v; } 
    }; 

// worker class specialization -- convert 'unsigned char' to 'int' 
template<> struct worker<unsigned char> 
    { 
    typedef int type; 
    static type get(unsigned char const & v) { return v; } 
    }; 

// mapper function 
template< typename V > typename worker<V>::type mapper(V const & v) 
    { 
    return worker<V>::get(v); 
    } 

int main() 
    { 
    char a='A'; 
    unsigned char b='B'; 
    cout << "a=" << mapper(a) << ", b=" << mapper(b) << "\n"; 
    } 

In diesem Beispiel verursacht die Spezialisierung der unsigned char es so zu einem int umgewandelt werden, dass es cout als Zahl angezeigt wird, anstatt als ein Zeichen, die folgende Ausgabe erzeugen, ...

a=A, b=66 
+0

Dies hat die gleiche zugrunde liegende Mechanik wie @ Xeos Antwort, könnte aber etwas sauberer sein, da nur eine Spezialisierung erforderlich ist. Ein weiteres Feature, das implementiert wird, ist, dass der Normalfall im Wesentlichen ein NOP ist - es gibt einfach einen Verweis auf den ursprünglichen Wert zurück. – nobar

+0

Ich mag diese Lösung auch. Wenn Sie die allgemeine Lösung ausdrücklich verbieten und nur explizite Vorlagen zulassen möchten, kann die erste Struktur in nur 'Vorlage struct worker; ', no body geändert werden. – Eyal

Verwandte Themen