2016-03-29 7 views
3

Ich habe einen Fehler beim Überladen std::wostream::operator<<() für std::string. Hier ist die minimale Testfall mein Problem darstellt:"no match" und "lvalue nicht binden" Fehler beim Überladen `operator <<` mit `std :: wostream` und` std :: string`

#include <string> 
#include <sstream> 

inline std::wostream &operator<<(std::wostream &os, const std::string &) 
{ 
    return os; 
} 

class FakeOstream{}; 

namespace mynamespace { 

class FakeClasse1 { 
    friend inline FakeOstream &operator<<(FakeOstream &out, const FakeClasse1 &) { 
     return out; 
    } 
}; 

class FakeClasse2 { 
    friend inline FakeOstream &operator<<(FakeOstream &out, const FakeClasse2 &) { 
     return out; 
    } 
}; 

void test() 
{ 
    auto mystring = std::string{u8"mystring"}; 
    std::wostringstream s; 
    s << mystring; // The errors occur here 
} 

} // namespace mynamespace 

Der Code kompiliert werden kann und hier ausgeführt: http://cpp.sh/9emtv

Wie man hier sehen kann, gibt es eine Überlastung für operator<< mit std::wostream und std::string. Die beiden falschen Klassen sind bis auf die Deklaration und operator<< mit FakeOstream und sich selbst leer. Die test() Funktion instanziieren Sie eine std::wostringstream und füttern Sie eine std::string. Die gefälschten Klassen und die Testfunktion befinden sich in einem Namespace.

Dieser Code ergibt die folgenden Fehler auf cpp.sh an der Linie s << mystring;:

In function 'void mynamespace::test()': 
25:10: error: cannot bind 'std::basic_ostream<wchar_t>' lvalue to 'std::basic_ostream<wchar_t>&&' 
In file included from /usr/include/c++/4.9/istream:39:0, 
       from /usr/include/c++/4.9/sstream:38, 
       from 2: 
/usr/include/c++/4.9/ostream:602:5: note: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = wchar_t; _Traits = std::char_traits<wchar_t>; _Tp = std::basic_string<char>]' 
    operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x) 
    ^

Bei direkten g ++ (Version 5.3.0 von MSYS2) verwendet wird, ein no match error auch angezeigt:

./tmpbug.cpp: In function 'void mynamespace::test()': 
./tmpbug.cpp:25:7: error: no match for 'operator<<' (operand types are 'std::wostringstream {aka std::__cxx11::basic_ostringstream<wchar_t>}' and 'std::__cxx11::basic_string<char>') 
    s << mystring; 
    ^
In file included from C:/Appli/msys64/mingw64/include/c++/5.3.0/istream:39:0, 
       from C:/Appli/msys64/mingw64/include/c++/5.3.0/sstream:38, 
       from ./tmpbug.cpp:2: 
C:/Appli/msys64/mingw64/include/c++/5.3.0/ostream:628:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = wchar_t; _Traits = std::char_traits<wchar_t>; _Tp = std::__cxx11::basic_string<char>] <near match> 
    operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x) 
    ^
C:/Appli/msys64/mingw64/include/c++/5.3.0/ostream:628:5: note: conversion of argument 1 would be ill-formed: 
./tmpbug.cpp:25:10: error: cannot bind 'std::basic_ostream<wchar_t>' lvalue to 'std::basic_ostream<wchar_t>&&' 
    s << mystring; 
     ^
In file included from C:/Appli/msys64/mingw64/include/c++/5.3.0/istream:39:0, 
       from C:/Appli/msys64/mingw64/include/c++/5.3.0/sstream:38, 
       from ./tmpbug.cpp:2: 

Soweit ich weiß, sind alle Teile des Beispiels notwendig, damit die Fehler erscheinen. Wenn ich den Namespace, die falschen Klassen oder nur einen der operator<< in den falschen Klassen auskommentieren, kompiliert der Code ganz gut. Außerdem, wenn ich nur eine der gefälschten Klassen oder die Test-Funktion außerhalb des Namespace, verschieben, wird der Code auch gut kompilieren.

Zusätzlich habe ich versucht, dieses Beispiel auf clang 3.7 mit dem Compiler von http://cppreference.com zu kompilieren, und der Code scheint ohne Probleme zu kompilieren.

Gibt es ein Problem mit meinem Code oder ist das ein GCC-Fehler? Wenn dies ein GCC-Fehler ist, ist da ein Workaround?

+0

Wenn der obere Abschnitt des Codes? – NathanOliver

+0

Der Code wird nicht kompiliert, zumindest nicht in GCC. Die Fehler treten in der Zeile 'mystring;' auf. Ich habe die Frage bearbeitet, um diese Genauigkeit hinzuzufügen. – Basile

+0

Sieht aus wie ein Namens-Lookup-Problem für mich, aber ich kann nicht erraten, was das richtige Verhalten ist. Das Hinzufügen von 'using :: operator <<;' zu 'test 'ist die minimale Problemumgehung, die ich gefunden habe. (Das Hinzufügen der Überladung zu 'std', um die argumentabhängige Suche zu aktivieren, macht das Problem auch weg, aber ich bin nicht sicher, wie gültig das ist.) – molbdnilo

Antwort

3

Dies ist eine schlechte Idee:

inline std::wostream &operator<<(std::wostream &os, const std::string &) 

wie Sie sollten nicht überlasten Operatoren auf zwei Arten in std, die auf eigene Faust nicht abhängen (außerhalb von std oder build-in) -Typen. Doing ... funktioniert nicht gut. Und meiner Meinung nach sollte das nicht erlaubt sein.

Egal, können Sie das gleiche Problem erzeugen mit dem Code entspricht, indem Sie einfach Ihre eigenen Namensraum notstd und eigene Art notstd::string, dann in der globalen Stammnamespace zu schaffen

inline std::wostream &operator<<(std::wostream &os, const notstd::string &) 
{ 
    return os; 
} 

definieren und die gleichen Symptome bekommen. Das macht also nichts aus.


Operatoren werden zuerst über unqualifiziertes Namenssuchen, dann über Argument abhängiges Nachschlagen gefunden.

Da wir keine using-Anweisung haben, sucht die Suche nach unqualifizierten Namen zuerst im umschließenden Namespace.Wenn nichts gefunden wird, werden die Namespaces, die es enthalten (und schließlich die Datei/der globale Namespace), dann durchsucht.

ADL ergänzt dies mit Operatoren, die über ADL oder Koenig-Lookup gefunden werden - es sucht in den Namespaces der Argumente und ihrer Template-Parameter.

nun die friend operator<< Sie definiert im Namensraum leben tun, um ihre Klasse enthält, aber sie in der Regel schwer zu finden sind.

Irgendwie ist Ihre Doppeldeklaration von friend operator<< macht Ihren Code sie zu finden, und aufhören, in den globalen Namespace für eine << zu suchen.

Für mich sieht das wie ein Fehler aus. Keines dieser „Koenig Betreiber“ sollte auf Moor-Standard unqualifizierte Namen-Suche mit Typ in keinem Zusammenhang mit den Klassen sichtbar sein, sie sind „enthalten“ in

MCVE.

#include <iostream> 
#include <sstream> 

namespace notstd { 
    struct string {}; 
} 

inline void operator<<(std::wostream &os, const notstd::string &){ return; } 

class FakeOstream{}; 

namespace mynamespace { 

    class UnusedClass1 { 
    friend inline void operator<<(FakeOstream &out, const UnusedClass1 &) { return; } 
    }; 

    class UnusedClass2 { 
     // comment this line out and the code compiles: 
    friend inline void operator<<(FakeOstream &out, const UnusedClass2 &) { return; } 
    }; 

    void test() { 
    auto mystring = notstd::string{}; 
    std::wostringstream s; 
    s << mystring; // The errors occur here 
    } 

} // namespace mynamespace 

int main(){} 

live example.

@ T.C. gefunden, was diese Fehler werden feststehend erscheint: Welcher Code kompiliert dann die Fehler erzeugt

test code

fix in gcc

+0

Das ist nicht illegal; Sie können das Ding nicht in 'namespace std' stecken, also ist es albern und sinnlos, aber nicht illegal. –

+0

@ T.C. Zuletzt habe ich überprüft, dass Sie die Operatoren nicht überladen können, es sei denn, die Typen hängen von etwas außerhalb von 'std' und/oder integrierten Typen ab. Oder habe ich diese Fehlerauflösung falsch gelesen? – Yakk

+0

Über welche Fehlerauflösung sprechen wir? –

Verwandte Themen