Wie schreibe ich einen Streaming-Operator, der willkürliche Container (vom Typ "X") nehmen kann?

8

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> :

implementiert %Vor%

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.)

    
Drew Dormann 05.12.2012, 13:51
quelle

5 Antworten

5

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:

%Vor%

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.

    
Yakk 07.12.2012, 20:26
quelle
3
%Vor%

Dies funktioniert nicht, wenn auf Ihrem Implementierungsvektor, in der Liste usw. mehr als zwei Vorlagenparameter vorhanden sind.

    
Armen Tsirunyan 05.12.2012 14:14
quelle
2

Einfach, wenn nicht elegant - und die nächste Person, die Ihren Code pflegt, könnte einen Mangel an ausgefallenen Vorlagen schätzen! In der Praxis würde ich die "Print" -Methode in einem cpp oder zumindest einem Detail -Namespace verstecken.

%Vor%     
Zero 10.12.2012 14:06
quelle
0

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.

    
razeh 10.12.2012 14:15
quelle
-1

Während @razeh eine nette Lösung bietet, können Sie Folgendes tun, wenn Sie sich für einen Container mit X gegenüber einem Container mit Y interessieren und spezialisierter drucken müssen:

%Vor%     
Zero 10.12.2012 14:57
quelle

Tags und Links