SFINAE: Wissen, ob eine Funktion bereits existiert oder nicht

8

Grundsätzlich möchte ich Code wie folgt schreiben:

%Vor%

Dies ist nicht möglich, da für operator<<(ostream&, vector)

keine Überladung vorliegt

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%     
Antoine Morrier 13.07.2017, 16:30
quelle

2 Antworten

4

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.

    
Yakk 13.07.2017, 18:14
quelle
4

Sie könnten ein kleines Erkennungs-Idiom schreiben, das testet, ob der Ausdruck stream << value wohlgeformt * ist.

Hier wird std::ostream verwendet:

%Vor%

Jetzt können Sie Ihre Überladung für operator<< wie folgt schreiben:

%Vor%

und ein Test

%Vor%

Demo

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.

    
AndyG 13.07.2017 17:30
quelle

Tags und Links