Grundsätzlich möchte ich Code wie folgt schreiben:
%Vor% Dies ist nicht möglich, da für operator<<(ostream&, vector)
Also, ich schreibe eine Funktion, die den Job macht:
%Vor% Das funktioniert gut, aber ich habe ein Problem mit der Zeichenfolge. Weil Strings iterierbar sind und Strings HAVE operator<<
function.
Also habe ich mit einem anderen Merkmal wie !is_streamable_out && _is_iterable
getestet, etwa so: std::declval<std::ostream&>() << std::declval<T>()
und wenn es Start- / Ende-Funktionen hat. Es funktioniert gut auf MSVC, aber nicht auf Clang (ich denke, es ist, weil der Compiler die Funktion, die ich gerade erstelle, verwendet, so dass eine Überladung für alle Methoden verfügbar ist).
Also, ich verwende derzeit !is_same_v<string, T>
, aber es ist nicht perfekt IMHO.
Gibt es eine Möglichkeit zu wissen, ob eine Funktion existiert, ohne die Funktion neu zu deklarieren?
Grundsätzlich möchte ich so etwas machen
%Vor%Es ist nicht das gleiche Problem wie Ist es möglich, eine Vorlage zu schreiben, um nach der Existenz einer Funktion zu suchen? , weil in diesem anderen Thread die Funktion nicht exakt gleich ist (toString vs toOptionalString). In meinem Fall sind die Funktionen identisch.
Hier ist mein vollständiger Code:
%Vor%und die wichtigsten:
%Vor% Wie man diesen Satz vermeidet, ist falsch in einer Vorlage SFINAE? bietet eine Antwort, die Ihr Problem löst - Überladung <<(ostream&, Ts...)
, die mit niedrigerer Priorität gefunden wird als jede andere <<
Überladung.
Gleichzeitig würde ich sagen, dass Ihr Plan ein schlechter ist. Das Überladen von Operatoren für std
types ist aus zwei Gründen ein schlechter Plan.
Erstens sollten Sie nur zögern, Operatoren für Typen, die Sie nicht besitzen, zu überlasten, es sei denn, es gibt einen wichtigen Grund.
Zweitens, wenn Sie dies tun, sollten Sie es im Namensraum des Typs tun, und Sie können Ihre <<
nicht in namespace std
injizieren, ohne Ihr Programm schlecht zu machen.
Operatoren, die in einem anderen Namespace als den fraglichen Typen überladen sind, können nur im Namespace gefunden werden, in dem Sie die Überladung ausgeführt haben. Operatoren, die in einem mit den Argumenten verknüpften Namespace überladen sind, können überall gefunden werden.
Dies führt zu fragilem <<
, das nur in einem Namensraum funktioniert.
Tun Sie das stattdessen:
%Vor%Wenn geschachtelte Vektoren nun funktionieren sollen, müssen Sie einen Stream-Bereich implementieren, der einen Adapter auf seinen Inhalt anwendet.
Live-Beispiel mit diesem Testcode:
%Vor% Ausgabe ist 1,2,3,4
.
Wie man das rekursiv macht:
Wir können eine adapt_for_streaming
-Funktion schreiben, die entweder an identity
(für bereits streambare Objekte) und an stream_range
für Dinge, die iterierbar, aber nicht bereits streambar sind, sendet.
Benutzer können dann neue adapt_for_streaming
Überladungen für andere Typen hinzufügen.
stream_range_t
streamt dann mit adapt_for_streaming
auf seinen Inhalt.
Als nächstes können wir Tupel / Paar / Array-Überladungen zu adapt_for_streaming
hinzufügen, und plötzlich kann std::vector< std::vector< std::tuple<std::string, int> > >
gestreamt werden.
Endbenutzer können stream_range
direkt oder adapt_for_streaming
aufrufen, um einen iterierbaren Container zu zeichnen. Sie können stream_range
sogar direkt auf einem std::string
aufrufen, um es wie eine streambare Sammlung von char
anstatt einer Zeichenfolge zu behandeln.
Sie könnten ein kleines Erkennungs-Idiom schreiben, das testet, ob der Ausdruck stream << value
wohlgeformt * ist.
Hier wird std::ostream
verwendet:
Jetzt können Sie Ihre Überladung für operator<<
wie folgt schreiben:
und ein Test
%Vor%Yakk weist auf eine interessante Sache in ihrer Antwort hin. Dieser Satz ist falsch .
Grundsätzlich, indem !can_print_v
verwendet wird, um eine Überladung für operator<<
zu aktivieren, sollte can_print_v
später true
sein, aber es ist false
, weil die erste Instanziierung der Vorlage dazu führte, dass die Struktur von% co_de abgeleitet wurde %. Nachfolgende Tests für std::false_type
sind daher ungültig.
Ich lasse diese Antwort als eine warnende Geschichte. Das Merkmal selbst ist in Ordnung, aber es ist nicht in Ordnung, es zu SFINAE zu verwenden, was das Merkmal ungültig macht.
* Es scheint, dass OP diesen Code in ihre eigene Codebasis kopiert und dann die Frage modifiziert hat, um sie einzuschließen, falls Sie sich fragen sollten, warum es umgekehrt aussieht.