Naja, das geht schneller als man denkt:
Ein Container für Key-Value Paare, für den es einen Datentyp gibt, der eine Spezialisierung verlangt und der sowohl als Key, als auch als Value möglich ist:
1 generische Version
+ 1 partielle Spezialiserung für Pointer
+ 1 volle Spezialiserung für den gesondert zu behandelnden Datentyp
+ 1 volle Spezialiserung für Pointer auf den gesondert zu behandelnden Datentyp
+ 1 partielle Spezialiserung für Pointer auf const
+ 1 volle Spezialiserung für den gesondert zu behandelnden Datentyp
+ 1 volle Spezialiserung für Pointer auf const auf den gesondert zu behandelnden Datentyp
Schon sind wir bei 7 Versionen und das pro template-Parameter.
Wenn man nun aber alles kombinieren können will, also solch lustige Dinge wie eine Spezialisierung für Pointer auf cont als Key und Pointer auf const auf den gesondert zubehandelenden Datentype als Value, dann bedeutet das nun dummerweise aber, dass die Zahl der benötigten Spezialisierungen 6 hoch n + 1 geneirsche Version beträgt. Bei den angesprochenen Key-Value Containern ergibt das also sage und schreibe 36 Spezialisierungen + 1 generische Version.
Falls man für irgendendeinen Container 3 Template-Parameter brauchen solle, wären es da schon 216 Spezialiserungen des Templates + 1 generische Version, usw.
Ich habe das ganze jetzt folgendermaßen gelöst:
Das Klassentemplate gibts nur einmal als generische Version mit einem überschaubaren öffentlichen Interface und dafür für jede Funktioin, für die es nötig ist, ein private Memberfunktionstemplate mit allen nötigen Spezialisierungen. Das sorgt zwar für einen riesigen haufen private Funktionen, aber zum einen bleibt die public API übersichtlich, zum anderen sind tatsächlich nur von den Funktionen mehre Imlementationen nötig, welche tatsächlich utnerschiedliche Implementationen verlangen, und nicht von jeder Funktion, wie bei einem Klassentemplate.
Das Problem, dass partielle Spezialiserungen vonFunktionstemplates vom Standard nicht erlaubt sind ich dabei mit Hilfe des Functor-Idioms (
http://accu.org/index.php/journals/385 - Abschnitt "Second Alternative") und das dabei entstehende Problem, dass volle Spezialiserungen von Membertemplates anderer Templates vom Standard nicht erlaubt sind, in dem ich sie mit Hilfe von zusätzlichen ungenutzten Dummy-Tempalte-Parametern zu partiellen Spezialisierungen mache. Glücklicherweise ist die dabei entstehende eines Obfuscated C++ Contests würdige Syntax (Beispiel gefällig? Deklaration:
template<typename FKeyType, typename FValueType, typename FDummyType> struct typesToStringImplementation {JString& operator()(JString& retStr) const;}; Definition:
template<typename EKeyType, typename EValueType> template<typename FKeyType, typename FValueType, typename FDummyType> JString& Dictionary<EKeyType, EValueType>::typesToStringImplementation<FKeyType, FValueType, FDummyType>::operator()(JString& retStr) const { return retStr = Helpers::TypeName::get<FKeyType>() + L", " + Helpers::TypeName::get<FValueType>();} Aufruf:
typesToStringImplementation<EKeyType, EValueType, void>()(retStr);) ein komplett hinter der sauberen API verstecktes Implementationsdetail.
Was mich immer noch sehr stört, ist das Problem, dass immer, wenn ich eine Pointerspezialisierung brauche, ich jedes Mal 2 redundante partielle Spezialisierungen schreiben darf: einmal für ppointer auf non-const und einmal für pointer auf const, weil der Aufrufer sonst jedes Mal const-Casten muss, damit auch die Pointer-Spezialisierung und nicht die gerische Version aufgerufen wird.