2009-08-01 7 views
4

ich meine Templat-Container-Klasse haben, die wie folgt aussieht:C++: Standardwerte für andere Template-Argumente als die letzten?

template< 
    class KeyType, 
    class ValueType, 
    class KeyCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<KeyType>, 
    class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType> 
> 
    class MyClass 
    { 
     [...] 
    } 

Was bedeutet, dass, wenn ich ein Objekt dieser Klasse instanziiert, ich kann es mehrere verschiedene Arten tun:

MyClass<MyKeyType, MyValueType> myObject; 
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject; 
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject; 

alle, sind gut. Das Problem tritt auf, wenn ich eine MyClass instanziieren möchte, die eine nicht standardmäßige Version des ValueCompareFunctor-Arguments verwendet, aber ich möchte weiterhin den Standardwert des KeyCompareFunctor-Arguments verwenden. Dann muß ich schreiben:

MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject; 

Es wäre viel bequemer, wenn ich irgendwie das dritte Argument weglassen könnte und nur schreiben, um diese:

MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject; 

Da das MyCustomValueCompareFunctor nur auf Objekte vom Typ MyValueType arbeitet und nicht auf Objekte vom Typ MyKeyType, es scheint, als könnte der Compiler zumindest theoretisch herausfinden, was ich hier meinte.

Gibt es eine Möglichkeit, dies in C++ zu tun?

+0

Sie könnten vielleicht etwas durch Metaprogrammation Tricks tun. Aber denken Sie darüber nach, was passiert, wenn Ihr Schlüssel und Werttyp identisch sind. Warum nicht eine Typedef für Ihre Instanziierung verwendet? (Beachten Sie, dass C++ 0X Template-Aliase hat, die Ihnen in Zukunft helfen könnten.) – AProgrammer

+1

Sie haben das wahrscheinlich schon versucht: Wenn der KeyCompareFunctor häufiger verwendet wird, dann wechseln Sie in der Definition KeyCompareFunctor und ValueCompareFunctor. Auf diese Weise können Sie den letzten Vorlagenparameter einfach weglassen. – Indy9000

+0

Siehe http://www.informit.com/articles/article.aspx?p=31473 –

Antwort

5

Im Allgemeinen, sowohl in Templates und Funktionen oder Methoden, C++ können Sie Standard verwenden für (und damit weglassen) nur Trailing Parameter - kein Ausweg.

Ich empfehle eine Vorlage oder ein Makro zu verkürzen AnObnoxiouslyLongSequenceOfCharacters<MyKeyType> zu Foo<MyKeyType> - nicht perfekt, aber besser als nichts.

4

No. Die nächstgelegene Sie kommen können, ist Benutzer zu erlauben, einigen Sentinel Typen angeben - wie void - „Standard-Wert hier“ bedeutet, und Vorlage metamagische innerhalb Ihrer Klasse typedef den realen Standard zu verwenden, wenn void Sie gegeben wurde . Aber das ist wahrscheinlich keine gute Idee aus Sicht der Lesbarkeit.

3

Boost parameters und Boost graph named parameters sind Bemühungen, Parameter für Template-Funktionen/Methoden zu benennen. Sie geben die Möglichkeit, Argumente in der von Ihnen bevorzugten Reihenfolge zu liefern. Einige Argumente können optional sein und Standardwerte haben.

Derselbe Ansatz kann auch auf Vorlagenargumente angewendet werden. Erstellen Sie Ihre Klasse mit N + 1 Vorlagenargumenten, anstatt N Vorlagenargumente + P optionale Argumente zu haben. Der letzte wird "benannte" Parameter enthalten, die weggelassen werden können.

Diese Antwort ist noch nicht vollständig, aber ich hoffe, es ist ein guter Anfang!

0

Eine alternative Möglichkeit ist Traits-Klassen zu verwenden:

template <class KeyType> 
class KeyTraits 
{ 
    typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare; 
}; 

template <class ValueType> 
class ValueTraits 
{ 
    typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> Compare; 
}; 

template<class KeyType class ValueType> 
class MyClass 
{ 
    typedef KeyTraits<KeyType>::Compare KeyCompareFunctor; 
    typedef ValueTraits<ValueType>::Compare KeyCompareFunctor; 
}; 

Dann, wenn Sie einen Typ haben, die eine andere Vergleichsfunktion für Key benötigt, dann würden Sie explizit die KeyTraits für diesen Fall geben spezialisieren. Hier ist ein Beispiel, wo wir es für int ändern:

template <> 
class KeyTraits<int> 
{ 
    typedef SpecialCompareForInt Cmopare; 
}; 
0

Es gibt eine andere Option ist, die Vererbung verwendet und die funktioniert wie folgt aus. Für die letzten beiden Argumente wird eine Klasse verwendet, die virtuell von einer Klasse mit zwei Elementvorlagen erbt, die zum Generieren der erforderlichen Typen verwendet werden können.Da die Vererbung virtuell ist, werden die deklarierten typedefs unter der Vererbung wie unten gezeigt geteilt.

template<class KeyType, 
     class ValueType, 
     class Pol1 = DefaultArgument, 
     class Pol2 = DefaultArgument> 
class MyClass { 
    typedef use_policies<Pol1, Pol2> policies; 

    typedef KeyType key_type; 
    typedef ValueType value_type; 
    typedef typename policies:: 
     template apply_key_compare<KeyType>::type 
     key_compare; 
    typedef typename policies:: 
     template apply_value_compare<ValueType>::type 
     value_compare; 
}; 

Nun haben ein Standardargument, die Sie verwenden, die typedefs für die Standard-Argumente, die Sie bieten möchten hat. Die Mitgliedsvorlagen werden von den Schlüssel und Werttypen parametriert werden

struct VirtualRoot { 
    template<typename KeyType> 
    struct apply_key_compare { 
    typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> 
     type; 
    }; 
    template<typename ValueType> 
    struct apply_value_compare { 
    typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> 
     type; 
    }; 
}; 

struct DefaultArgument : virtual VirtualRoot { }; 

template<typename T> struct KeyCompareIs : virtual VirtualRoot { 
    template<typename KeyType> 
    struct apply_key_compare { 
    typedef T type; 
    }; 
}; 

template<typename T> struct ValueCompareIs : virtual VirtualRoot { 
    template<typename ValueType> 
    struct apply_value_compare { 
    typedef T type; 
    }; 
}; 

Nun use_policies wird von allen Schablonen Argumente abzuleiten. Wenn eine abgeleitete Klasse von VirtualRoot ein Element aus der Basis versteckt, ist dieses Mitglied der abgeleiteten Klasse dominant gegenüber dem Element der Basis und wird verwendet, auch wenn das Basisklassenelement von einem anderen Pfad im Vererbungsbaum erreicht werden kann .

Beachten Sie, dass Sie für die virtuelle Vererbung nicht bezahlen, da Sie nie ein Objekt vom Typ use_policies erstellen. Sie verwenden die virtuelle Vererbung nur, um die Dominanzregel zu verwenden.

template<typename B, int> 
struct Inherit : B { }; 

template<class Pol1, class Pol2> 
struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2> 
{ }; 

Weil wir möglicherweise aus der gleichen Klasse ableiten mehr als einmal verwenden wir eine Klassenvorlage Inherit: direkt zweimal die gleiche Klasse Vererben ist verboten. Aber es indirekt zu erben ist erlaubt. Sie können jetzt alles wie folgt verwenden:

MyClass<int, float> m; 
MyClass<float, double, ValueCompareIs< less<double> > > m; 
Verwandte Themen