Partielle Template-Spezialisierung
-
So in etwa sähe das von dir beabsichtigte aus:
template<typename Key_Type, typename Mapped_Type, typename Compare, typename Allocator> struct print_functor<typename std::map<Key_Type, Mapped_Type, Compare, Allocator>::value_type, std::map<Key_Type, Mapped_Type> > : public std::unary_function<typename std::map<Key_Type, Mapped_Type, , Allocator>::value_type, void> { private: typedef typename std::map<Key_Type, Mapped_Type, Compare, Allocator>::value_type value_type; public: void operator()(const value_type& el) { std::cout << el.first << "\t\t" << el.second << '\n'; } };Allerdings gibts eines zu beachten:
Der Standard schreibt nicht vor, dass std::map nur diese vier Template-Parameter hat. Sollte es noch mehr geben, dann funktioniert diese Spezialisierung nicht. Diese implementierung ist also nicht unbedingt portabel.
-
template<typename ContainerT> // Container reicht bei weitem struct print_functor : public std::unary_function<typename ContainerT::value_type, void> { void operator()(const typename ContainerT::value_type& el) { std::cout << el << '\n'; } }; template<typename K, typename V, typename C, typename A> struct print_functor<std::map<K, V, C, A> > : public std::unary_function<std::pair<K const, V>, void> { void operator ()(std::pair<K const, V> const& el) { std::cout << el.first << "\t\t" << el.second << '\n'; } }; // ... template<typename ContainerT> void print_container(const ContainerT& container) { print_functor<ContainerT> a_print_functor; std::for_each(container.begin(), container.end(), a_print_functor); }Dies dürfte eigentlich reichen und funktionieren. Auch können hier
std::mapverarbeitet werden, welche einen anderen Vergleich- oder Allokatortyp haben.Allerdings ist es nicht 100% portabel, da der Standard nicht garantiert, dass
std::mapnur 4 Templateparameter hat. Bei den meisten Standardbibliotheken ist dies allerdings so. Mir ist nicht mal eine bekannt, wo dies nicht der Fall ist.Edit: Da war pumuckl zum Teil etwas schneller

Grüssli
-
Tausend Dank euch, Jungs!

Das Problem, vor dem ich jetzt aber stehe, ist die Frage, wie ich den Funktor instanziieren soll.
Am liebsten wäre mir eine einheitliche Schnittstelle, d. h., print_container() soll für alle Container gleich aufgerufen werden können. Weil sich der Funktionsrumpf von print_container() abhängig vom Container aber unterscheidet, gibts dann Mehrfachdefinitionen.Vermutlich stell ich mich da einfach nur extrem ungeschickt an; ich hantiere aber erst seit kürzerem mit Templates und finde das teilweise noch etwas verzwickt.
-
Wo steht denn, dass eine map nicht genau 4 template-parameter haben muss?
C++ Standard - 23.3.1 Template class map schrieb:
- 1 A map is a kind of associative container that supports unique keys (contains at most one of each key value) and provides for fast retrieval of values of another type T based on the keys. Map supports bidirectional iterators.
- 2 A map satisfies all of the requirements of a container and of a reversible container (23.1) and of an associative container (23.1.2). A map also provides most operations described in (23.1.2) for unique keys. This means that a map supports the a_uniq operations in (23.1.2) but not the a_eq operations.
For a map<Key,T> the key_type is Key and the value_type is pair<const Key,T>. Descriptions are provided here only for operations on map that are not described in one of those tables or for operations where there is additional semantic information.
namespace std { template < class Key, class T, class Compare = less<Key>, class Allocator = allocator< pair<const Key,T> > > class map;Woraus schließt ihr diese Aussage also?
bb
-
@moagnus,
Verstehe dein Problem nicht. Du kannst das Funktionstemplateprint_containernehmen, welches ich im Code hingeschrieben habe. Da gibt es keine Mehrfachdefinition, ausser du machst etwas gehörig falsch, so falsch, dass ich es mir gar nicht vorstellen kann und daher keine Idee habe, was du falsch gemacht haben könntest
@unskilled,
Wir schliessen es aus der einfachen Tatsache, dass nirgends im Standard steht, dassstd::mapnicht auch noch zusätzliche Templateparemeter mit Defaultparametern haben darf. Es steht eben nichts drin
Grüssli
-
imho ist der aufbau damit explizit gegeben - aber ok, wenn das sonst drin stehen müsste, habt ihr wohl recht ^^
bb
-
Dravere schrieb:
@unskilled,
Wir schliessen es aus der einfachen Tatsache, dass nirgends im Standard steht, dassstd::mapnicht auch noch zusätzliche Templateparemeter mit Defaultparametern haben darf. Es steht eben nichts drin
Das sehe ich nicht so. Wenn dort die Deklaration des Templates unmissverständlich mit
template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T> > > class map;beschrieben wird, gehe ich davon aus, dass eine Implementierung sich daran zu halten hat. Die "as-if"-Regel wäre bei anderen Deklarationen, die weitere Default-Parameter besitzen, auch verletzt.
Gruß,
SP
-
Als Alternative zu den Funktoren, bei denen bereits der Typ drei Zeilen Code braucht:
template <typename T> void output_element(const T& value) { std::cout << value << std::endl; } template <typename First, typename Second> void output_element(const std::pair<First, Second>& pair) { std::cout << pair.first << " " << pair.second << std::endl; } template <typename Container> void output_container(const Container& c) { for (Container::const_iterator iter = c.begin(), end = c.end(); iter != end; ++iter) { output_element(*iter); } }Oder wie gesagt den
operator<<fürstd::pairüberladen (vielleicht braucht man das ja gerade sonst irgendwo), dann reicht eine Element-Ausgabefunktion und man kann mitstd::for_each()arbeiten. Hey, jeweils nur eine Zeile pro Funktion, und keine Verschmutzung durch etlichetypenames.
template <typename First, typename Second> std::ostream& operator<< (std::ostream& stream, const std::pair<First, Second>& pair) { return stream << pair.first << " " << pair.second; } template <typename T> void output_element(const T& value) { std::cout << value << std::endl; } template <typename Container> void output_container(const Container& c) { std::for_each(c.begin(), c.end(), output_element<Container::value_type>); }
-
@Sebastian Pizer,
Lies mal Kapitel 1.4 (Implementation compliance) des Standards. Dort steht, dass diese Beispiele nur teilweise Definitionen sind und dass eine Bibliothek diese Beispielimplementationen auch erweitern darf, sofern das Programm durch die Veränderungen mit der gezeigen Form im Standard trotzdem korrekt ist. Deshalb müssen die zusätzlichen Templateparameter auch Defaultparameter haben, sonst würden sie dagegen verstossen.Edit: Kapitelname noch eingefügt.
Grüssli
-
Dravere schrieb:
... Deshalb müssen die zusätzlichen Templateparameter auch Defaultparameter haben, sonst würden sie dagegen verstossen.
"dagegen"? Wogegen denn genau?
Ich kann Kapitel 1.4 nichts entnehmen, was mit Deiner Interpretation vereinbar ist.Bzgl Klassen und Klassen-Templates steht da
For classes and class templates, the library clauses specify partial definitions. Private members (clause 11) are not specified, but each implementation shall supply them to complete the definitions according to the description in the library clauses.
Dass dort keine kompletten Definitionen spezifiziert sind, ist klar. Aber wenn ich eine Deklaration eines Klassentemplates ohne irgendwelche in kursiv gesetzte Platzhalter oder Bemerkungen sehe, ist das für mich eine Deklaration, an der nicht gerüttelt werden darf.
Gruß,
SP
-
Sebastian Pizer schrieb:
[...]wenn ich eine Deklaration eines Klassentemplates ohne irgendwelche in kursiv gesetzte Platzhalter oder Bemerkungen sehe, ist das für mich eine Deklaration, an der nicht gerüttelt werden darf.
So seh ich das auch...
-
@Sebastian Pizer,
Was du machst ist eine eigenständige Interpretation, es steht aber nicht so im Standard und es müsste explizit stehen, damit es so ist. Denn im Standard wird alles genau festgelegt. Um deine Behauptung also zu beweisen, musst du mir im Standard zeigen, wo steht, dass die Implementation sich 1:1 an diese Codebeispiele zu halten haben. Vor allem in diesem Punkt in Bezug auf Templateparametern.Zusätzlich steht übrigens auch im Kapitel 1.4:
A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.
Es dürfen Erweiterungen existieren. Das Programm muss einfach nur well-formed bleiben. Es steht im Standard, dass
std::map4 Templateparameter haben soll. Man muss also schreiben dürfen:std::map<A, B, C, D>.
Dies verbietet allerdings nicht eine solche Deklaration:template < typename A, typename B, typename C = std::less<A>, typename D = std::allocator<std::pair<A const, B> >, typename X = int /*, ... */ > class map { // ... };Grüssli
-
Dravere schrieb:
Es dürfen Erweiterungen existieren. Das Programm muss einfach nur well-formed bleiben. Es steht im Standard, dass
std::map4 Templateparameter haben soll. Man muss also schreiben dürfen:std::map<A, B, C, D>.Sagt wer?
Ist das hier auch well-formed?#include <map> template<template<class,class,class,class> class Foo> struct blupp {}; int main() { blupp<std::map> x; }Wo ziehst Du denn da die Grenze?
Gruß,
SP
-
Sebastian Pizer schrieb:
Dravere schrieb:
Es dürfen Erweiterungen existieren. Das Programm muss einfach nur well-formed bleiben. Es steht im Standard, dass
std::map4 Templateparameter haben soll. Man muss also schreiben dürfen:std::map<A, B, C, D>.Sagt wer?
Ist das hier auch well-formed?#include <map> template<template<class,class,class,class> class Foo> struct blupp {}; int main() { blupp<std::map> x; }Opops.Mein erstes Statement zu dem Thema war falsch. Natürlich kann man die Geschichte mit der map<X, Y> bzw. map<X, Y, C, A> machen - das ist kein Problem. Anders ists mit dem template template Parameter den du hier gezeigt hast. Ich weiß nicht mehr obs Meyers in einem der Effective C++ Bücher oder Sutter in einem der Exceptional C++ Bücher war - aber genau dieses Beispiel taucht dort auf. Eben weil die Standardcontainer zusätzliche (per default gesetzte) Templateparameter haben dürfen kann es passieren, dass der Compiler std::map nicht mit einem 4-Parameter-Template matchen kann.
-
@Sebastian Pizer,
Es gibt nirgendwo eine Garantie, dassstd::mapgenau 4 Templateparameter hat, nur dass man ganz sicher 4 angeben kann. Das ist ein Unterschied. Deshalb ist dein Programm einfach nicht Standardkonform.Sebastian Pizer schrieb:
Wo ziehst Du denn da die Grenze?
Die Grenze zieht der Standard und zum Teil ist keine vorhanden.
@pumuckl,
Bist du sicher dass es funktioniert? Ich hatte irgendwas im Kopf, dass es unter speziellen Umständen nicht funktionieren kann. Shade Of Mine hat da mal vor langer Zeit etwas präsentiert gehabt. Kann mich nur nicht mehr richtig erinnern und finde es aktuell auch nicht mehr...
Deshalb habe ich, um meine Aussage abzusichern, den Satz noch dazugeschrieben
Grüssli
-
Dravere schrieb:
@Sebastian Pizer,
Es gibt nirgendwo eine Garantie, dassstd::mapgenau 4 Templateparameter hat, nur dass man ganz sicher 4 angeben kann. Das ist ein Unterschied. Deshalb ist dein Programm einfach nicht Standardkonform.Wieso ist es das nicht? Woran machst Du das fest? Welcher Satz aus dem Standard lässt Dich das glauben? Du ziehst willkürlich eine Grenze zwischen map<int,int,comp,my_alloc> muss funktionieren aber als std::map muss nicht als template<class,class,class,class>class-Parameter benutzbar sein.
So, wie ich das sehe, beziehen sich die Zitate, die Du gebracht hast, auf Definitionen (Räpresentierung ist implementierungsabhängig), und nicht die Deklarationen.
Gruß,
SP
-
Absolute Klasse! Echt.
Am elegantesten finde ich Nexus' zweite Lösung mit den überladenen Ausgabeoperatoren.
Draveres Vorschlag dagegen hat mir in Sachen generischer Programmierung wirklich weitergebracht: Da wurde mir erstmal klar, dass eintemplate<typename K, typename V, typename C, typename A> struct print_functor<std::map<K, V, C, A> >mit
print_functor<ContainerT> a_print_functor;instanziiert werden kann und der Compiler dann die Zeile mit "template<typename... >" ganz automatisch anhand der übergebenen map "ausfüllt".
Die Diskussion über den Standard war auch ganz informativ.
Danke natürlich auch an alle anderen!
-
*gerade noch bemerkt, dass er da noch nicht geantwortet hat*
Da dem moagnus nun geholfen ist, dürfen wir hier auch sicher ohne Gewissensbisse weiterdiskustieren

Sebastian Pizer schrieb:
Wieso ist es das nicht? Woran machst Du das fest? Welcher Satz aus dem Standard lässt Dich das glauben?
Ich wiederhole mich:
Es gibt im ganzen Standard keinen Satz, welcher aussagt, dassstd::mapgenau 4 Templateparemeter haben muss. Es werden 4 präsentiert, aber nirgends gesagt, dass es nicht mehr sein dürfen. Ich kann dir daher keinen Satz zeigen, weil es eben keinen gibt.
Es gibt nur den Code, allerdings ist dieser nicht verbindlich, sondern soll nur zur Verdeutlichung dienen, was wunderbar auch im Standard steht. Im Standard steht aber eben nicht, dass er verbindlich ist.Sebastian Pizer schrieb:
Du ziehst willkürlich eine Grenze zwischen map<int,int,comp,my_alloc> muss funktionieren aber als std::map muss nicht als template<class,class,class,class>class-Parameter benutzbar sein.
Wenn hier jemand willkürlich ist, dann bist es du, da du einfach willkürlich annimmst, dass der präsentierte Code verbindlich ist.
Sebastian Pizer schrieb:
So, wie ich das sehe, beziehen sich die Zitate, die Du gebracht hast, auf Definitionen (Räpresentierung ist implementierungsabhängig), und nicht die Deklarationen.
Und das ist dann auch deine willkürliche Interpretation. So steht es nicht im Standard. Du fügst dies einfach dazu, weil es dir logisch erscheint, aber so steht es nicht da. Und entscheidend ist im Standard, was dort steht. Der Standard geht nicht davon aus, dass jemand logische Zusätze reinbaut, so dass es einem besser gefällt. Sonst wäre der Standard ja sinnlos

Am einfachsten machen wir es also so, dann bin ich sofort bereit dir zu glauben:
Zeig mir eine Stelle im Standard, wo steht, dass der angegebene Code verbindlich ist.Grüssli
-
Dravere, ich verstehe Deine Position, stimme Dir aber nicht zu, weil es für mich einfach nicht nach einer schlüssigen Argumentation aussieht. Du leitest von Kapitel 1.4 irgendwie ab, dass ein Klassentemplate zusätzliche Defaultparameter haben darf weil angeblich nur
typedef std::map<dings,bums,comp,alloc> t;bei entsprechenden Typen dings,bums,comp,alloc funktionieren müsse, nicht aber die Verwendung des Klassentemplates als Template-Parameter des Typs
template<class,class,class,class>class. Ich finde das ganz schön weit hergeholt, wenn man sich dazu nochmal Absatz 3 und 8 anguckt:3 For classes and class templates, the library clauses specify partial definitions. Private members (clause 11) are not specified, but each implementation shall supply them to complete the definitions according to the description in the library clauses.
8 A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.
Absatz 3 --> Der Standard spezifiert partielle Definitionen. Partiell heißt hier, dass es Lücken und Platzhalter gibt, die eine Implementierung sinvoll zu füllen hat um die anderen Spezifikationen zu erfüllen. Hier steht nichts, was einem suggerieren könnte, dass eine konforme Implementierung veränderte Deklaration verwenden kann.
Absatz 8 --> Wenn ich mich richtig erinnere, hast Du diesen Absatz auch zitiert und das Hinzufügen von weiteren Defaultparametern als "Extension" gewertet. Der Standard fordert, dass Erweiterungen nicht das Verhalten von wohlgeformten Programmen ändern dürfen. Ein Programm, welches std::map als template-template-parameter mit 4 eigenen template parametern benutzen will, ist ohne diese Erweiterung wohlgeformt, mit aber leider nicht. Daraus schließe ich, dass eine konforme Implementierung keine zusätzlichen Defaultparameter für Klassentemplates einführen kann.
Gruß,
SP
-
Sebastian Pizer schrieb:
Du leitest von Kapitel 1.4 irgendwie ab, dass ein Klassentemplate zusätzliche Defaultparameter haben darf weil angeblich nur
typedef std::map<dings,bums,comp,alloc> t;bei entsprechenden Typen dings,bums,comp,alloc funktionieren müsse, nicht aber die Verwendung des Klassentemplates als Template-Parameter des Typs
template<class,class,class,class>class.Das ist genau der Fehler, ich leite nichts ab. Ich sage nur, dass nichts drin ist. DU bist der, welcher irgendwelche Dinge probiert herzuleiten

Du hast mir zum Beispiel immer noch nicht gezeigt, wo im Standard steht, dass der präsentierte Quellcode verbindlich ist. Du hast mir im Standard nicht gezeigt, wo steht, dass es genau 4 Parameter sein müssen. Zeig mir diese Stellen!
Sebastian Pizer schrieb:
Absatz 3 --> Der Standard spezifiert partielle Definitionen. Partiell heißt hier, dass es Lücken und Platzhalter gibt, die eine Implementierung sinvoll zu füllen hat um die anderen Spezifikationen zu erfüllen. Hier steht nichts, was einem suggerieren könnte, dass eine konforme Implementierung veränderte Deklaration verwenden kann.
Kannst du mir sagen, woher du dies nimmst? Ist das deine freie Interpretation des Satzes? Mit ein wenig zusätzlichen Pfeffer und Salz, sonst würde es nicht so schmecken?
Und ja, in dem Satz steht nicht viel. Grundsätzlich steht da nur, dass der Standard für Klassen und Klassentemplates eine partielle Definition liefert. Private Member werden nicht gezeigt, es ist Aufgabe der Implementation diese gemäss den Angaben im Standard zu ergänzen.
Es steht nirgends, dass es Lücken und Platzhalter hat.Das ist der einzige Ort, wo irgendetwas über den gezeigten
std::mapCode steht. Hier steht nicht, dass der gezeigte Code verbindlich ist. Es relativiert sogar den gezeigten Code um einiges.Sebastian Pizer schrieb:
Absatz 8 --> Wenn ich mich richtig erinnere, hast Du diesen Absatz auch zitiert und das Hinzufügen von weiteren Defaultparametern als "Extension" gewertet. Der Standard fordert, dass Erweiterungen nicht das Verhalten von wohlgeformten Programmen ändern dürfen. Ein Programm, welches std::map als template-template-parameter mit 4 eigenen template parametern benutzen will, ist ohne diese Erweiterung wohlgeformt, mit aber leider nicht. Daraus schließe ich, dass eine konforme Implementierung keine zusätzlichen Defaultparameter für Klassentemplates einführen kann.
Das ist einfach nur falsch. Es ist nicht im Standard definiert, dass
std::mapgenau 4 Templateparameter hat. Du hast mir dies ja auch immer noch nicht gezeigt.
Was du hier machst, ist rekursiv. Du definierst für dich, dassstd::mapgenau 4 Templateparameter hat, obwohl es nirgends im Standard so steht, und mit dieser fehlerhaften oder nicht bewiesenen Annahme beweist du dir dann, dassstd::mapgenau 4 Templateparameter haben muss.Ich wiederhole mich nocheinmal:
BEWEISE MIR, DASS IRGENDWO IM STANDARD STEHT, DASSSTD::MAPGENAU 4 TEMPLATEPARAMETER HAT.
(nicht als schreien auffassen, sondern als jedes einzelne Wort klar und deutlich ausgesprochen :D)Denn du hast dies bisher nicht getan. Weil man dies nicht beweisen kann. Weil es nicht im Standard steht.
Grüssli