2016-06-23 19 views
3

Ist es möglich, Klassenelementfunktionen basierend auf angegebenen Enum-Werten zu überladen oder zu spezialisieren?Überlastung (oder Spezialisierung) von Klassenmitgliedern basierend auf verschiedenen Enum-Werten

enum class Type { 
    TypeA, 
    TypeB, 
    TypeC 
}; 

class Foo { 

    public: 

    template <Type t, typename R = std::enable_if_t<t==Type::TypeA, int>> 
    R get() { 
    return 1; 
    } 

    template <Type t, typename R = std::enable_if_t<t==Type::TypeB, double>> 
    R get() { 
    return 2; 
    } 

    template <Type t, typename R= std::enable_if_t<t==Type::TypeC, float>> 
    R get() { 
    return 3; 
    } 
}; 

Foo foo; 

std::cout << foo.get<Type::TypeA>() << std::endl; 
std::cout << foo.get<Type::TypeB>() << std::endl; 
std::cout << foo.get<Type::TypeC>() << std::endl; 

Die Kompilierung beschweren sich über Überladung auf obigen Code-Snippet.

+0

Was ist die Fehlermeldung? Ich denke du kannst das machen. Außerdem steht 'std :: enable_if_t' nicht in' C++ 11'. –

+0

Welcher Compiler ist das? MSVC unterstützt dies noch nicht. – SirGuy

Antwort

5

Ein hübscher Standard Weg, es zu beheben, ist die Klausel std::enable_if in den Rückgabetyp der Funktion, anstatt in die Vorlage Parameter wie diese.

Dies kompiliert für mich bei C++ 11 Standard.

#include <iostream> 
#include <type_traits> 

enum class Type { 
    TypeA, 
    TypeB, 
    TypeC 
}; 

class Foo { 

    public: 

    template <Type t> 
    typename std::enable_if<t==Type::TypeA, int>::type get() { 
    return 1; 
    } 

    template <Type t> 
    typename std::enable_if<t==Type::TypeB, double>::type get() { 
    return 2; 
    } 

    template <Type t> 
    typename std::enable_if<t==Type::TypeC, float>::type get() { 
    return 3; 
    } 
}; 

static_assert(std::is_same<int, decltype(std::declval<Foo>().get<Type::TypeA>())>::value, ""); 
static_assert(std::is_same<double, decltype(std::declval<Foo>().get<Type::TypeB>())>::value, ""); 
static_assert(std::is_same<float, decltype(std::declval<Foo>().get<Type::TypeC>())>::value, ""); 

int main() { 

Foo foo; 

std::cout << foo.get<Type::TypeA>() << std::endl; 
std::cout << foo.get<Type::TypeB>() << std::endl; 
std::cout << foo.get<Type::TypeC>() << std::endl; 

} 

Ich bin mir nicht sicher, ob ich im Detail erklären kann, warum diese Änderung so einen Unterschied für den Compiler macht.

Beachten Sie jedoch Folgendes. Obwohl Sie mit Ihrer Version niemals get mit zwei expliziten Template-Parametern instanziieren, können technisch alle drei Member-Funktionen-Templates kollidieren und Funktionen mit exakt demselben Namen erzeugen. Denn wenn Sie get<Type::TypeB, int> instanziiert haben, würde es denselben Rückgabetyp, dieselben Eingabeparameter und denselben Namen haben wie get<Type::TypeA>. C++ unterstützt keine Funktionsvorlagenspezialisierung, es würde Überladungsauflösungsregeln sehr kompliziert machen. Wenn also Funktionsvorlagen mit dem Potenzial zum Kollidieren vorliegen, kann der Compiler sehr verärgert sein.

Wenn Sie es so machen, wie ich es gezeigt habe, besteht keine Möglichkeit, dass die Vorlagen kollidieren und eine Funktion mit demselben Namen und derselben Signatur erzeugen.

+0

Der Grund, warum es einen Unterschied macht, ist, dass in Ihrem Beispiel jede Funktion eine andere Signatur hat. Im ursprünglichen Beispiel haben sie alle die gleiche Signatur, da Standardparameter für Mustervorlagen nicht Teil der Signatur sind. Es betrachtet es als die gleiche Funktion mehrfach zu definieren. –

3

Sie können die Verwendung von std::enable_if vermeiden, dass die Type Kartierung und Rückgabetypen eine einfache Struktur spezialisiert (Bar, im folgenden Beispiel)

#include <iostream> 

enum class Type { 
    TypeA, 
    TypeB, 
    TypeC 
}; 

template <Type t> 
struct Bar; 

template <> 
struct Bar<Type::TypeA> 
{ using type = int; }; 

template <> 
struct Bar<Type::TypeB> 
{ using type = double; }; 

template <> 
struct Bar<Type::TypeC> 
{ using type = float; }; 


class Foo { 

    public: 

     template <Type t> 
     typename Bar<t>::type get(); 
}; 

template <> 
Bar<Type::TypeA>::type Foo::get<Type::TypeA>() 
{ return 1; } 

template <> 
Bar<Type::TypeB>::type Foo::get<Type::TypeB>() 
{ return 2.2; } 

template <> 
Bar<Type::TypeC>::type Foo::get<Type::TypeC>() 
{ return 3.5f; } 


int main() 
{ 
    Foo foo; 

    std::cout << foo.get<Type::TypeA>() << std::endl; 
    std::cout << foo.get<Type::TypeB>() << std::endl; 
    std::cout << foo.get<Type::TypeC>() << std::endl; 

    return 0; 
} 
0

Ihr Beispiel nicht kompilieren, weil ausgefallene Art Templat Parameter nicht Teil sind der Funktionssignatur. Soweit es den Compiler betrifft, definieren Sie die gleiche Funktion mehrmals, was illegal ist. Stattdessen müssen Sie einen voreingestellten Nicht-Typ-Vorlagenparameter verwenden.

class Foo { 

    public: 

    template <Type t, std::enable_if_t<t==Type::TypeA, int> = 0> 
    int get() { 
    return 1; 
    } 

    template <Type t, std::enable_if_t<t==Type::TypeB, int> = 0> 
    double get() { 
    return 2; 
    } 

    template <Type t, std::enable_if_t<t==Type::TypeC, int> = 0> 
    float get() { 
    return 3; 
    } 
}; 

Zwei funktionierende Lösungen sind bereits veröffentlicht; also warum habe ich diesen hier gepostet? Nun, ich mag es besser. Um konkret zu sein:

  • Es behält, was die Funktion tatsächlich nimmt und gibt getrennt davon zurück, wenn es aktiviert ist. Dies erleichtert das Lesen und Verstehen der Funktion. Sie können das Code-Snippet aus dem zweiten Argument in ein kleines Makro mit dem Namen REQUIRE oder ähnlichem ziehen; Das macht deutlich, was vor sich geht. Keine der beiden anderen Antworten hat diese Eigenschaft.
  • Diese Technik ist universeller; es kann in leicht unterschiedlichen Situationen mit Konstrukteuren verwendet werden, was alles nicht zurück und kann nicht mit den anderen beiden Lösungen verwendet werden

max66 Ansatz der Enum auf eine Art von Abbildung ist schön und es ist etwas, das Sie sollten sich bewusst sein von. Es ist jedoch angemessener, wenn Sie den Körper generisch schreiben können; Wenn Sie jedes Implementierungsgremium trotzdem einzeln ausschreiben müssen, fügt es nur einen Textbaustein (IMHO) hinzu.Sie sollten sich auch bewusst sein, dass eine auf Spezialisierung basierende Lösung fragil ist; Es funktioniert nicht, wenn ein zweiter Vorlagenparameter vorhanden ist oder die Klasse Foo eine Klassenvorlage ist, aufgrund der Einschränkungen für Spezialisierungsfunktionsvorlagen.

Mit dem Makro ich oben erwähnt habe man es auf diese Weise schreiben konnte:

template <Type t, REQUIRE(t==Type::TypeA)> 
    int get() { 
    return 1; 
    } 
    // ... 

Was ich denke, ist so gut wie es geht.

+0

Down Wähler, um zu erklären? –