VS 6.0 -> VS 2008: Verhalten bei temp. std::stringstream-Objekt



  • Hallo!

    Wir testen gerade, ob unsere Abteilung ein Upgrade von VS 6.0 (SP5) auf VS 2008 (SP1) machen kann/sollte (*endlich*).

    Folgender Code läuft unter VC++ 6.0 wie gewünscht, unter VS 2008 aber nicht mehr:

    #include <sstream>
    #include <iostream>
    
    class AutoOut: public std::wostringstream{
    
          // Konstruktor/Destruktor //
       public:
          AutoOut(){
    
          }
          ~AutoOut(){ 
              std::wcout << str() << std::endl;
          }
    }
    
    inline AutoOut say(){
    
        return AutoOut();
    }
    
    #include "AutoOut.h"{
    
    int wmain(int argc, wchar_t* argv[]){
    
        say() << L"Hello World!";
    }
    

    Die Funktion say() erstellt ein temporäres Objekt von AutoOut, welches von std::stringstream abgeleitet ist. Nach dem Aufruf wird das Objekt zerstört und dabei wird der Inhalt durch std::wcout ausgegeben.

    In VC++ 6.0 funktioniert das einwandfrei. In VS2008 allerdings wird nur die Adresse des Stringliterals ausgegeben.

    So funktioniert es auch in VS2008:

    #include "AutoOut.h"{
    
    int wmain(int argc, wchar_t* argv[]){
    
        {
            AutoOut out = say();
            out << L"Hello World!";
        }
    }
    

    Bei der ursprünglichen Variante springt er bei VS2008 immer zur std::ostream-Methode die void* als Argument besitzt und gibt nur die Adresse aus.

    Warum? Und wie kann ich das abstellen?


  • Mod

    1. Es ist nicht iostreams sind nicht entwickelt worden um mit temporären Objekten zu arbeiten.
    2. Das Problem ist, dass dein temporärer iostream ein rvalue ist, der aus der Funktion zurückkommt. Alle temporären Objekte sind IMHO rvalues und damit const!
    Es gibt zwei überladene Funktionen für operator<<, die hier nun ins Spiel kommen. Eine member Funktion die einen void* nimmt und eine non-member Funktion, die einen wchar_t* annimmt.
    Deine Objekt ist aber ein rvalue (const), also ist dies kein Kandidat für die non-member Funktion (siehe Überladungsregeln). Allerdings hindert nichts daran das der const wchar_t* implizit auf const void* für die member Funktion konvertiert wird.
    Also wird es als void* behandelt.

    Wenn Du also brutal den rvalue wegcastest, funktioniert es wie erwartet:

    template<typename T>
    T & make_lvalue(T const & x)
    {
      return const_cast<T &>(x);
    }
    
    make_lvalue(say()) << L"Hello World!";
    

    Unterschied zum VC6?
    Der VC6 und sein STL und Template Implementierung waren falsch und buggy.
    Dein Code hätte unter VC6 schon nicht gehen dürfen!

    Anmerkung: Also 100% sicher bin ich mir nicht.
    Aber evtl. gibt es ja nochmehr Gurus hier, die den Standard inhaliert haben.


Anmelden zum Antworten