Datentypenkonvertion???



  • Erstmal die kleinigkeiten:

    [cpp]
    // long --> int
    intVal = static_cast<long>(longVal)
    // oder (veraltet und böse)
    intVal = (long) longVal;

    // char * --> char
    // ist keine "Konvertierung" im Sinne des Sprachstandards,
    // sondern ein "dereferenzieren eines Zeigers"
    char * pChars = ...;
    char c;
    if (pChars != NULL)
    *c = pChars;
    [/cpp]

    zum Thema int --> string sollte ich mich besser nicht äußern... die superdupervariante wäre

    #include <string>
    #include <sstream>
    
    int intVal;
    std::stringstream stream;
    stream << intVal;
    std::string s = stream.str();
    

    Andere Varianten sind itoa, printf, und Konvertierungsfunktionen von "proprietären" Stringklassen (z.B. CString::Format)

    jetzt allgemeiner:

    Unterscheiden wir erstmal drei Typem von Typumwandlung:

    a) Implizit - macht der Compiler automatisch
    b) Explizit - macht der Compiler, wenn du's ihm sagst
    c) Kontext-Abhängig: explizit z.B. über eine Funktion

    a) Implizite Umwandlungen sind z.B.:

    char --> short --> int --> long
    float --> double
    T * --> T const *

    Beispiel

    extern void Foo(long l);
    
    int main()
    {
      int i = 0;
      Foo(i);  // <-- implizite Umwandlung int --> long
    }
    

    Üblicherweise wird der Compiler
    - warnen, wenn bei einer impliziten Konvertierung Daten/Genauigkeit verloren gehen kann (z.B. double -> float)
    - erroren 🙄 wenn es mehrere nicht eindeutige implizite konvertierungen gibt

    Implizite Konvertierungen für eigene Klassen sind möglich a) durch einen Konstruktor mit einem Parameter und b) durch einen member operator TYPE(). Dies ist allerdings mit vorsicht zu genießen.

    Explizite Umwandlungen
    ähnlich den obigen, man aber dem Compiler explitzit das man umwandeln will.
    Da gibt es in C++ vier Arten:

    static_cast - casten anhand der zur Compile-Zeit vorhandenen Typinformation. mit

    double dbl = 12345;
    long l = static_cast<long>(dbl);
    

    Wird z.B. ein double korrekt in einen long umgewandelt (l==12345)

    dynamic_cast - casten anhand der zur Laufzeit bekannten Typinformation (über C++ RTTI == Run Time Type Information für Klassen). Notwending um sicher von einem Zeiger auf Basisklasse zu einem Zeiger auf abgeleitete Klasse zu casten.

    const_cast - um "const"-Modifikatioren wegzubekommen (wie wird man eigentlich ein "volatile" los? [edit]Auch mit const_cast - danke Hume[/edit])

    reinterpret_cast - geht nur für Pointer, Umwandlung ohne Berücksichtigung irgendwelcher Typinformation - z.B. int * in long *

    In Ansi C wurden alle diese cast-typen (außer dynamic_cast natürlich) wie folgt ausgedrückt:

    double dbl = 12345;
    long l = (long) dbl;
    

    Welche "Art" von Cast ist für C problemlos aus dem Kontext ersichtlich. Ist in C++ (bis auf dynamic_cast) im Prinzip immer noch so, aber nicht mehr empfehlenswert.

    c) Kontextabhängige Umwandlungen
    Umwandlungen, die nicht nur von Quell- und Zieltyp abhängen, sondern noch zusätzliche Kontextinformationen brauchen.
    In C++ wären das z.B. int <--> string (Kontextinformation: Basis, Tausendertrennzeichen u.a.)

    Scriptsprachen bieten für solche Casts meistens eine implizite Standard-Konvertierung

    [edit] Noch ein paar korrekturen angebracht (siehe unten)[/edit]



  • Hi,
    super beitrag, im faq ist es nicht so gut erklärt.
    ➡
    verschiebt doch das ding und löscht meinen schrott heraus 😃

    cu max



  • kleiner Tipp:
    wenn ich long in int umwandeln will, dann caste ich nach int und nicht nach long...



  • [EDIT]Unsinniger Beitrag[/EDIT]



  • Vielen dank an alle!
    Vor allem an 'Peterchen'.. hat geholfen.



  • const_cast - um "const"-Modifikatioren wegzubekommen (wie wird man eigentlich ein "volatile" los?)

    Mit dem const_cast. const_cast hätte also eigentlich cv_cast heißen müssen, da du mit ihm cv-Qualifikation hin und weg casten kannst.



  • static_cast - casten anhand der zur Laufzeit vorhandenen Typinformation

    Das halte ich für etwas Mißverständlich, da du bei dynamic_cast das selbe schreibst. Der static_cast ist aber im Gegensatz zum dynamic_cast nicht auf Laufzeitinformationen angewiesen.

    Was imo beim static_cast noch erwähnenswert wäre ist, dass er immer den Rückweg einer impliziten Konvertierung durchführen kann. Sprich:
    Integraler-Typ nach enum, void-Ptr nach Daten-Ptr, Base-nach-Derived...

    BTW: Das Beispiel für den static_cast ist zwar in der Tat eine explizite Konvertierung, allerdings ist sie hier überflüssig. double -> long ist schließlich auch eine implizite Konvertierung.

    reinterpret_cast - geht nur für Pointer, umwandlung ohne Berücksichtigung irgendwelcher Typinformation - z.B. void * in long *

    Das stimmt nicht. Eine Konvertierung von void* nach long* macht der static_cast.
    reinterpret_cast ist für nicht verwandte Typen. Wie z.B. int* nach long*.



  • static_cast: ups sorry - typo. Hab's korrigiert - static_cast : zur Compile-Zeit bekannte Typinformation.

    für den Rest - Danke



  • bin auch neu in c++ und hab ein Problem, ich hab ein TEdit und will den wert in einer double variable speichern, geht aber nicht, auch wenn ich von String auf double geh, geht nicht. Bitte helft mir.



  • Mit dem const_cast. const_cast hätte also eigentlich cv_cast heißen müssen, da du mit ihm cv-Qualifikation hin und weg casten kannst.

    Muss man zum hin-casten tatsächlich einen const_cast bemühen?



  • Helium schrieb:

    Mit dem const_cast. const_cast hätte also eigentlich cv_cast heißen müssen, da du mit ihm cv-Qualifikation hin und weg casten kannst.

    Muss man zum hin-casten tatsächlich einen const_cast bemühen?

    Ab und zu schon. Wenn du z.B. eine Klasse Foo hast, die ein const-überladenes Interface func() und func() const besitzt, du nur ein nicht konstantes Objekt foo besitzt, dennoch aber func() const aufrufen willst, dann hilft dir z.B. ein const_cast das benötigte const zu erhalten. Alternativ kannst du natürlich auch eine neue Referenz-auf-const anlegen und dein Objekt an selbige binden, sprich auf die implizite non-const-nach-const-Konvertierung bauen.

    Ein konkretes Beispiel hatten wir hier letztens im Zusammenhang mit Iteratoren. Da hatte jemand einen const_reverse_iterator und einen nicht konstanten STL-Container. Auf diesem hat er rend() aufgerufen (wo natürlich die non-const-Variante bevorzugt wurde) und wollte das Ergebnis mit dem const_reverse_iterator vergleichen. Leider gab es keinen passenden Operator !=.
    Eine Möglichkeit das Problem zu beheben war ein const_cast<const STLConatiner&>(stlContainer).rend().



  • zum "hin"-casten natürlich nicht:

    int * --> const int * const
    und
    int * --> volatile int * volatile

    sind implizite Umwandlungen (da 100% sicher)



  • peterchen schrieb:

    zum "hin"-casten natürlich nicht:

    int * --> const int * const
    und
    int * --> volatile int * volatile

    sind implizite Umwandlungen (da 100% sicher)

    Sag mal hast du meinen Beitrag überhaupt gelesen?

    class Foo
    {
    public:
         void func() {}
         void func() const {}
    };
    
    int main()
    {
        Foo f;
        f.func();
    }
    

    Welche Methode wird hier wohl aufgerufen? Richtig. Die non-const-Version von func. Und wenn du jetzt aber die const-Version aufrufen willst?
    Dann machst du entweder ein const_cast und castest dir ein const hin oder aber du musst eine weitere Referenz erstellen:

    class Foo
    {
    public:
         void func() {}
         void func() const {}
    };
    
    int main()
    {
        Foo f;
        const_cast<const Foo&>(f).func();   // func() const
        const Foo& cf = f;                  
        cf.func();                          // func() const
    }
    

    Manchmal muss man halt auch eine implizite Konvertierung explizit anfordern.



  • gelesen ja - und jetzt sogar verstanden 😉

    Das Design der Klasse Foo ist aber im Regelfall fragwürdig - wenn der Bedarf besteht, zwischen beiden Varianten zu wählen, sollten das auch zwei verschiedene Funktionsname sein. Aber ich seh' ein, daß sich die Realität manchmal nicht an alle Regeln hält.



  • @Hume:
    Dann scheine ich nicht in der Lage zu sein auch nur einen einzigen Compiler aufzutreiben, der diesbezüglich Standardkonform ist (nagut, so viele habe ich dann auch wieder nicht getestet).

    Aber selbst der Comeau sagt mir
    "In strict mode, with -tused, Compile succeeded"

    und ich hab folgendes im Code:

    Foo foo;
    static_cast<const Foo>(foo).methode();
    

    Ich scheine also keinen const_cast zu benötigen. Der Grund, warum ich einen benötigen sollte, bliebe mir auch verborgen.



  • const_cast ist eingeschränkter - du kannst also explizit klarmachen daß du wirklich nur ein "const" loswerden willst. (kann dich auch vor fehlern schützen...)



  • const_cast ist eingeschränkter - du kannst also explizit klarmachen daß du wirklich nur ein "const" loswerden willst. (kann dich auch vor fehlern schützen...)

    War das auf mich bezogen? Wenn ja, dann verstehe ich es leider nicht. 😞

    Die einzige andere Möglichkeit, die mir spontan einfallen würde, um ein const wegzubekommen wäre ein c style cast, worüber wir hoffentlich gar nicht mehr diskutieren.
    Es ging um's Hinzufügen und da macht IMHO const_cast nicht wirklich Sinn. Ich lasse mich aber gerne eines besseren beleren.



  • @Helium
    Mal abgesehen, dass dein static_cast als Zieltyp eine Referenz haben sollte, hast du in dieser konkreten Situation natürlich recht. Ein static_cast kann ja immer den umgekehrten Weg einer impliziten Konvertierung gehen.

    Nur was machst du hier:

    void func(const char** p) {}
    
    int main()
    {
        char** p;
        func(p); // error
        func(static_cast<const char**>(p));    // error
        func(const_cast<const char**>(p));     // ok!
    
    }
    

    Also für mich sieht das so aus, als würde man hier ein const "hin casten". Aber vieleicht irre ich mich auch.



  • Nur was machst du hier:

    Ich denke zwar nicht, das du ernsthaft eine Antwort erwartet hast, aber ich gebe dir trotzdem eine

    void func(const char** p) {}
    
    int main()
    {
        char** p;
    
        func(&static_cast<const char*>(*p));     // ok!
    }
    


  • Helium schrieb:

    Nur was machst du hier:

    Ich denke zwar nicht, das du ernsthaft eine Antwort erwartet hast, aber ich gebe dir trotzdem eine

    void func(const char** p) {}
    
    int main()
    {
        char** p;
    
        func(&static_cast<const char*>(*p));     // ok!
    }
    

    "ComeauTest.c", line 7: error: expression must be an lvalue or a function designator
    func(&static_cast<const char*>(*p)); // ok!

    Ich denke hier kommst du um eine Hilfsvariable nicht drum herum. Allerdings ist mein Hirn gerade nicht an. Kann mich also auch täuschen. Wie auch immer: ich sehe deinen Punkt sowieso nicht.


Anmelden zum Antworten