2010-12-14 4 views
4

Einer meiner Klassen eine Templat-Funktion erklärt:Teil Spezialisierung des Operators()

template<class A, class B> 
A do_something(const std::vector<B> &data) 

, die ich teilweise mag auf typename A spezialisieren. B ist eine Familie von Typen, die eine ziemlich minimale Schnittstelle implementieren, und wir verwenden eine Menge von ihnen, also möchte ich, dass meine Spezialisierung auf B generisch ist. Ich vermute, dass dies doppelt ärgerlich ist, da typename A nur als Rückgabetyp verwendet wird.

Aus dem Internet habe ich nachgelesen, dass ich nicht teilweise eine Funktion spezialisieren kann, also habe ich eine Klasse wie folgt erstellt:

template<class A, class B> 
class do_something_implementation { 
    public: 
    do_something_implementation(const std::vector<B> &data_) { 
     data = data_; 
    } 

    int do_something_implementation<int, B>::operator()() { 
    /* Complicated algorithm goes here... */ 
    } 

    double do_something_implementation<double, B>::operator()() { 
    /* Different complicated algorithm goes here... */ 
    } 

    private: 
     std::vector<B> data; 
} 

Wenn ich versuche, das zu kompilieren (Visual Studio 2008), der Compiler stürzt ab() und ich bekomme die folgende Fehlermeldung:

fatal error C1001: An internal error has occurred in the compiler. 

ich nehme an, das ist mein Problem und nicht der Compiler. Gibt es eine bessere Möglichkeit, die angestrebte Teilspezialisierung auszudrücken?

+0

Wenn ein Compiler stirbt, ist es ein Problem mit dem Compiler - nicht in der Lage, Ihnen zu sagen, wie falsch Ihr Code ist. Es gibt ein paar Dinge in Ihrem Code falsch, aber das erklärt nicht eine "interne Fehler" Nachricht. Natürlich wäre die richtige Sache für den Compiler nicht, den Code trotzdem zu kompilieren ... aber es sollte eine bessere Diagnose liefern als "Ich habe zu Tode gehockt" –

+0

@David: Compiler-Autoren sind keine Götter von Bug Free Software! Dies kann oder wird möglicherweise nicht in einer späteren Version behoben. –

Antwort

7

Normalerweise ist es so geht:

template <typename A, typename B> 
struct DoSomethingHelper 
{ 
    static A doIt(const std::vector<B> &data); 
}; 

template <typename B> 
struct DoSomethingHelper<double, B> 
{ 
    static double doIt(const std::vector<B> &data) { ... } 
}; 

template <typename B> 
struct DoSomethingHelper<int, B> 
{ 
    static int doIt(const std::vector<B> &data) { ... } 
}; 

template<class A, class B> 
A do_something(const std::vector<B> &data) 
{ return DoSomethingHelper<A, B>::doIt(data); } 
+0

Sie haben vergessen, es statisch zu machen. – Puppy

+0

@DeadMG: Guter Fang, danke –

+0

Danke - das hat den Trick gemacht. –

1

Menschen in der Regel nur auf eine statische Implementierung weiterleiten.

template<class A, class B> class X; 
template<class A, class B> friend class X; 
template<class A, class B> class X { 
public: 
    static A do_something(class_type* not_this, const std::vector<B>& data) { 
     //... 
    } 
}; 
// partially specialize 
template<class A, class B> 
A do_something(const std::vector<B> &data) { 
    return X<A, B>::do_something(this, data); 
}; 
+0

Ich habe nie gesehen, dass Freund in diesem Kontext verwendet wurde. Was tut es? – Ferruccio

+0

@Ferruccio: Was Sie erwarten würden - Freunde X für alle Vorlage Argumente A und B. – Puppy

+0

'Fehler C2255: 'Freund': nicht erlaubt außerhalb einer Klassendefinition '... –

4

Nachdem Sie nun die klassisch sie auf statische Methode gesehen haben, ist es eigentlich ein andere Art und Weise, wenn der Typ für die spezialisieren „komplett“ ist .

Sie sind möglicherweise nicht in der Lage, eine Funktion teilweise zu spezialisieren, aber Sie können sie perfekt überlasten.

template <typename A, typename B> 
A do(std::vector<B> const& data) { return this->doImpl(data, (A*)0); } 

template <typename A, typename B> 
A doImpl(std::vector<B> const& B, A*) { // generic implementation } 

template <typename B> 
int doImpl(std::vector<B> const& B, int*) { // int implementation } 

template <typename B> 
double doImpl(std::vector<B> const& B, double*) { // double implementation } 

Der Trick ist es, ein „nicht verwendete“ Argument zu doImpl für den alleinigen Zweck der eigentlich die Auswahl der richtigen Implementierung (durch Überlastung Auflösung) zu übergeben.

Hier habe ich einfach (A*)0 übergeben, weil dies A 's Konstruktor nicht beteiligt (falls es nicht trivial ist).

Dieses Dispatch-Idiom wird in der STL verwendet, um einige Algorithmen mit besserer Effizienz für einige Iteratorkategorien zu implementieren (z. B.ist O (1) für zufällige Iteratoren).

Ich finde es viel leichter, dass die Verwendung einer Hilfsklasse mit statischen Methoden und Teilspezialisierungen ...aber vielleicht ist das nur ich :)

+0

intelligente Lösung. +1 für den guten Job! – Nawaz

+0

@Nawaz: Wie gesagt, es ist nur Wiederverwendung, was die STL, die ich zur Hand habe, bereits verwendet: p –

+0

das ist, was ich an dieser Lösung mochte. keine Notwendigkeit, neue Klassen/Strukturen zu definieren. :-) – Nawaz

1

keine Lösung für Ihr Problem (es gibt ein paar schon da), aber einige der Dinge, die in Ihrem Code falsch sind:

Sie vermissen ein struct oder class Stichwort in der Vorlage Klassendeklaration:

template <typename A, typename B> struct do_something_implementation { 
//        ^^^^^^ 

innerhalb der Klassendefinition, Member-Funktionen müssen nicht an einen qualifizierten Namen, unabhängig davon verwenden, ob die Klasse eine Vorlage ist oder nicht:

class A { 
    void A::foo() {} // Error, should be: void foo() {} 
}; 

Vorlage Spezialisierungen Mitglied nicht innerhalb der Klassendefinition erscheinen, aber im Namespace-Ebene:

class B { 
    template <typename T> void foo(T); 
}; 
template <> void B::foo<int>(int) {} 
template <> void B::foo<double>(double) {} 

Plus auf Ihrem Fall die Member-Funktion ist nicht eine Vorlage, sondern eine Nicht-Template-Member-Funktion (die Vorlage ist die enthaltene Klasse, nicht die Funktion selbst). Was Ihr Code effektiv zu tun versucht, ist die Definition der Member-Funktionen anderer Klassen innerhalb der allgemeinen Vorlage, was versucht wird.

Insgesamt gab es genug Fehler, um die Analyse des Codes fast unmöglich für den Compiler zu machen, was Sie versuchten zu tun und eine gute Fehlermeldung zu liefern, aber immer noch sollte eine Fehlermeldung auf die erste angezeigt werden Zeile, die du kopiert hast, anstatt dich zu Tode zu prügeln.

+0

Ja - der Code hier ist nicht das, was den Compilerfehler verursacht hat; Ich habe ein paar Dinge verpasst, weil ich den Code für die Frage vereinfacht habe. (Die "Klassen" -Definition zu vergessen ist albern!) –