cout.operator ...



  • Ahja, dann wäre die Theorie zumindest richtig 🤡

    thx



  • die << Operatoren für char* und Knnsorten sind freie Funktionen, andere (zB die für void*) sind als Memberfunktionen definiert.



  • Den Unterschied bei "cout.operator<<()" und "cout <<" gibts nicht.

    Das ist quatsch. Natürlich gibt es einen Unterschied. Ersteres ruft *immer* eine Memberfunktion operator<< der Klasse ostream auf. Letzteres ist syntaktischer Zucker und kann entweder
    a) eine Memberfunktion operator<< aufrufen oder
    b) eine freie Funktion operator<<, die als linkes Argument einen zu cout kompatiblen Typ erwartet.

    Die aufgetretene Verwirrung entsteht durch falsche Dokumentationen wie z.B. der MSDN. Zumindest in einer älteren Version (die ich hier habe) steht schwarz auf weiß, dass ostream einen Memberfunktion operator<< besitzt, die als Parameter einen const char* erwartet.
    Da die Konvertierung von "String" nach const char* besser ist als die nach void* (eine andere Memberfunktion operator<< erwartet einen void*), müsste hier also genau diese Memberfunktion operator<<(const char*) aufgerufen werden, welche dann die Zeichenkette "String" ausgeben sollte.

    Das Problem ist nun, dass die Dokumentation lügt. ostream (die Template-Klasse basic_ostream) besitzt *keine* solche Memberfunktion (weder laut Standard noch in der MS-Implementation). Vielmehr gibt es eine *freie* (Template-)Funktion operator<< die einen const char* als *zweites* Argument erwartet. Als erstes Argument erwartet sie ein basic_ostream-Objekt.
    Diese freie Funktion kann also für ein:

    cout << "Hallo";
    

    aufgerufen werden. Besser: sie kann nicht nur, sie wird auch aufgerufen:)

    Sie kann aber natürlich *nicht* für ein:

    cout.operator<<("Hallo");

    aufgerufen werden, da sie ja wie bereits gesagt keine Memberfunktion ist.
    Hier wird stattdessen die Memberfunktion operator<< aufgerufen, die einen void-Zeiger erwartet. Da jeder Objekt-Zeiger implizit nach void* konvertiert werden kann, ist dies zulässig. Diese Memberfunktion gibt aber gerade die Adresse aus auf die der Zeiger verweist.

    Für alle denen es negativ aufgefallen ist:
    Ja, das ist letztlich genau das was Bashar auch schon gesagt hat. Ich wollte es nur noch mal etwas ausführlicher aufschreiben 🙂

    [ Dieser Beitrag wurde am 25.06.2003 um 12:59 Uhr von HumeSikkins editiert. ]



  • thx - nun ist das klar, und die Frage nach dem "wie genau" hat sich auch erledigt 🙂

    Dannggääää



  • Ich wollte es nur noch mal etwas ausführlicher aufschreiben 🙂

    😃



  • Was mir grad zu dem Thema einfällt. Ich hab vor längerer Zeit mal so halbherzig ne Stringklasse angefangen. Dort habe ich IIRC den char*-operator überladen, so dass er nen zeiger auf den intern gespeicherten char zurückgibt. das hat auhc geklappt, aber cout << Mystring; hat mir immer die adresse des internen strings zurückgegeben, wohingegen cout <<(char*)Mystring; funktioniert hat. Falls da grad jemand ne kurze Erklärung hat, könnte er sie ja posten ...



  • Hallo,
    der Grund steht für den Template-Kenner mit zusätzlichen VC-Kentnissen etwas versteckt bereits in meinem oberen Beitrag 🙂

    Die freie Funktion operator<< die einen const char* ausgeben kann ist eine *Template*-Funktion und sieht beim VC so aus:

    template<class _E, class _Tr> inline
    basic_ostream<_E, _Tr>& operator<<(basic_ostream<_E, _Tr>& _O, const _E *_X)
    

    Bei einem:

    cout << "Hallo";
    

    wird _E zu char und _Tr zu char_traits<char>. Der Parameter _X ist also vom Typ
    const char*.

    Bei der Herleitung von Templateparameter finden aber grundsätzlich keine Benutzerdefinierte Konvertierungen statt.
    Der Aufruf:

    DeineStringKlasse s;
    cout << s;
    

    kann nicht zum Aufruf des entsprechenden Operators führen, da s nicht vom Typ const char* ist und der Konvertierungsoperator nicht berücksichtigt wird.

    Die Memberfunktion operator << die ein void*-erwartet ist kein Template sondern eine normale Funktion. Hier werden selbstverständlich alle erlaubten Konvertierungen durchgeführt.

    DeineStringKlasse s;
    cout << s;
    

    Hier wird also erst s nach char* mittels einer Benutzer-definierten Koonvertierung gewandelt und dann char* nach void* durch eine Standardkonvertierung.

    Bis hierhin ist der VC im Recht. Allerdings schreibt der Standard zusätzlich zu der oben genannten freien Funktion noch eine Funktion:

    [code type="c++"]
    template<class _E, class _Tr> inline
    basic_ostream<_E, _Tr>& operator<<(basic_ostream<_E, _Tr>& _O, const char* )
    

    vor. Da hier der zweite Parameter nicht von einem Templateparameter abhängt, werden für diesen auch benutzerdefinierte Konvertierungen durchgeführt.

    DeineStringKlasse s;
    cout << s;
    

    würde also diese Funktion aufrufen.

    Der VC versäumt allerdings die Bereitstellung einer solchen Funktion.



  • Was muss man denn nun machen, damit cout.operator << ("Hallo Welt") beim MSVC++ den String "Hallo Welt" ausgibt? 😕



  • operator<<(cout,"Hello World");
    aufrufen?



  • Man löst dieses Problem dann, indem man auch den <<-operator überlädt?



  • int main()
    {
    
      string s = "Hallo3";
    
      cout << "Hallo" << endl;
      cout.operator <<(L"Hallo1") << endl;
      cout.operator <<(L"Hallo2") << endl;
      cout.operator <<(s.c_str()) << endl;
      operator <<(cout,"Hallo4");
    
      cin.get();
      return 0;
    }
    

    😕 🙄



  • Man löst dieses Problem dann, indem man auch den <<-operator überlädt?

    Falls du noch dein Stringproblem meinst.
    Meine Lösung wäre:
    1. Man entfernt umgehend die implizite Konvertierung und ersetzt sie durch eine explizite Methode (à la c_str). Implizite Konvertierungen bringen häufig Probleme mit sich und es lohnt sich in dieser Situation einfach nicht diese in Kauf zu nehmen. Außerdem orientiere ich mich gerne an Designs, die sich bereits etabliert haben (std::string verzichtet z.B. auf eine implizite Konvertierung nach char*. Die MFC-Klasse CString hat eine solche, die Klassenbibliothek gilt aber nirgends auch nur halbwegs als Beispiel für gutes Design).

    2. Zusammen mit der Klasse liefert man auch einen operator<< der ein Objekt der Klasse ausgibt.

    Fertig. Weniger Mehrdeutigkeiten, weniger Überaschungen.



  • Original erstellt von HumeSikkins:
    **Die MFC-Klasse CString hat eine solche, die Klassenbibliothek gilt aber nirgends auch nur halbwegs als Beispiel für gutes Design).
    **

    Doch 🙂 Bei Marcus als Beispiel fürs Refcounting 🙂

    Naja, was kann es bei impliziten KOnvertierungen noch für Probleme geben? CString hat aber dazu ja auch noch ne Funktion analog zu c_str: GetBuffer.

    [ Dieser Beitrag wurde am 25.06.2003 um 22:05 Uhr von dEUs editiert. ]



  • Ich empfinde C++ an dieser Stelle als unbefriedigend und übermäßig kompliziert. Manche denken sogar, "<<" und "operator <<" seien das Gleiche. Offenbar gibt es hier zusätzlich Ungereimtheiten von Compiler zu Compiler.



  • siehe auch folgenden Wirrwarr: http://www.microsoft.com/msj/archive/SD71.aspx



  • Ich empfinde C++ an dieser Stelle als unbefriedigend und übermäßig kompliziert.

    Aha. Ich nicht. Was heißt das jetzt? Und wichtiger. Wie würdest du es besser machen. Natürlich unter den Bedingungen die für C++ gelten (z.B. maximale C-kompatibilität).

    Manche denken sogar, "<<" und "operator <<" seien das Gleiche

    Manche denken sogar, void main wäre Standard.
    Manchmal hilft *lernen* viel mehr als denken. C++ erfordert in vielen Bereichen schlicht und einfach wissen. Intuition allein kommt man in dieser Sprache nicht weit.

    siehe auch folgenden Wirrwarr

    Wieso wirrwarr? Dieses Thema wird in jedem *guten* Anfängerbuch erwähnt und erklärt. Immer auch mit der entsprechenden Lösung.

    Natürlich ist C++ komplex (aber das ist eigentlich jede mir bekannte Sprache general-purpose-Sprache).
    Auch besonders im Bereich der Auflösung von Funktionsaufrufen.
    Deshalb lernt man C++ auch nicht in 21 Tagen oder am Wochenende. Zumindest wenn man mehr als nur "Hello World" will.


Anmelden zum Antworten