Ich habe eine C ++ Klasse " X
", die eine besondere Bedeutung hätte, wenn ein Container von ihnen an ein std::ostream
gesendet werden würde.
Ich habe es ursprünglich speziell für std::vector<X>
:
Wenn ich std::ostream << std::deque<X>
oder std::ostream << std::set<X>
oder einen ähnlichen Containertyp unterstützen wollte, ist die einzige Lösung, die ich kenne, das Kopieren und Einfügen der gesamten Funktion und das Ändern nur der Funktionssignatur!
Gibt es eine Möglichkeit, operator << ( std::ostream &, const Container & )
generisch zu codieren?
(" Container
" wäre hier jeder Typ, der die kommentierte Beschreibung erfüllt.)
Wenn Sie diese Antwort bereits gelesen haben, können Sie die unten stehende ADL-Version verwenden. Es ist viel verbessert.
Erstens, eine kurze und süße Version, die so ziemlich funktioniert:
%Vor%, das nur Dinge erkennt, die wie int
und double
Container mit unterschiedlichen Überladungen aussehen. Ich würde empfehlen, die Implementierung von operator<<
zu ändern. ;)
Eine geeignetere Route (Danke @Xeo) wäre dieser Adl-Hack. Wir erstellen einen Hilfsnamensraum, in den wir begin
und end
von std
importieren, und dann einige Template-Funktionen, die argumentabhängige Lookups auf begin
und end
durchführen (die std
Version sehen wir nicht) eine enger gebundene), und dann diese aux::adl_begin
-Funktionen verwenden, um zu bestimmen, ob das, was uns übergeben wurde, als Container über X behandelt werden kann:
Damit zählen nicht nur die Arrays von double
s als Container über double
, sondern auch die Bereiche begin
und end
, die Iteratoren über das Double zurückgeben, das den Container übernimmt als Argument funktioniert auch. Dies entspricht, wie for(auto&& i:container)
lookup funktioniert (perfekt? Einigermaßen gut?), So ist eine gut funktionierende Definition von "container".
Beachten Sie jedoch, dass weniger aktuelle Compiler alle C ++ 11-Funktionen, die wir verwenden, haben, wenn wir weitere dieser Erweiterungen hinzufügen. Die obigen kompiliert in gcc 4.6 ich glaube, aber nicht gcc 4.5. *.
...
Und hier ist der ursprüngliche Funktionscode mit einem Test-Framework: (nützlich, wenn Ihr Compiler es aufgibt, können Sie sehen, wo es unten falsch läuft)
%Vor% etwa die Hälfte der oben genannten ist Testplatte. Die is_iterator_of_type
Vorlage und die operator<<
Überladungen sind was Sie wollen.
Ich nehme an, dass ein Container vom Typ T
eine Klasse mit einem typedef iterator
ist, dessen value_type
ein T
ist. Dies deckt jeden std
-Container und die meisten benutzerdefinierten Container ab.
Link zum Ausführungslauf: Ссылка - Beachten Sie, dass einige Compiler keine vollständige C ++ 11-SFINAE unterstützen und möglicherweise Unsinn erfordern um es zur Arbeit zu bringen.
Testfälle, die übriggelassen wurden, um jemandem zu helfen, zu überprüfen, welche Unterstützungsebene der Compiler für diese Techniken hat.
Dies funktioniert nicht, wenn auf Ihrem Implementierungsvektor, in der Liste usw. mehr als zwei Vorlagenparameter vorhanden sind.
Wenn Sie die Frage zum Bereitstellen eines speziellen Streaming-Verhaltens für jede Klasse, die einen bereichsbasierten Zugriff auf Widget bereitstellt, etwas neu definieren, ist eine Lösung:
%Vor% Dies funktioniert für std::vector
, std::list
, std::deque
und std::set
. Wenn Sie versuchen, etwas zu streamen, das keinen Bereichszugriff auf Widget bietet, sagen wir std::list<int>
, erhalten Sie einen Kompilierungsfehler, weil die const Widget-Referenz nicht an die Ints in std::list<int>
gebunden werden kann. Wenn Sie eine Überladung für den Operator & lt; & lt; Für std::list<int>
wird der Code kompiliert.