Frage zu uniform initialization



  • Hi,

    mir ist vorhin aufgefallen, dass ein (sozusagen) doppelter Konstruktoraufruf nicht funktioniert, zumindest nicht ohne geschweifte Klammern.
    Hier ein Beispiel: http://ideone.com/SOPczE

    Umklammere ich meinen Parameter nicht in geschweiften Klammern, kompiliert der Compiler meinen Code mit folgender Begründung nicht:

    error: invalid initialization of reference of type 'const MyStr&' from expression of type 'const char*'

    Meine Frage wäre: Wieso ist das so und wieso funktioniert es erst mit den zusätzlichen Klammern?



  • Weil der Standard es so sagt 😉

    Automatisch wird nur eine "Konvertierung" durchgeführt, du müsstest aber von
    const char* => std::string => MyStr

    Mit {} forderst du explizit eine Instanz von MyStr an, den Rest kann der Compiler dann automatisch machen.

    Du könntest auch:

    1. eine entsprechenden Konstruktor in MyStr einfügen
    using namespace std::string_literals;
    DoSth("Test"s);
    

    verwenden.



  • Okay, wenn das der Standard so sagt, dann muss ich das wohl so aktzeptieren.

    Ich war eben verwundert, da ich erwartet hätte dass der Kompiler eben schaut wie er aus meinem const char* ein MyStr erzeugen kann und daher sehen würde dass MyStr-ctor ein std::string erwartet und der std::string-ctor einen const char* aktzeptiert und diese Kette dann abläuft. Also im Grunde das gleiche Verhalten zeigt wie mit {}, nur eben ohne {} 😉
    Dass es funktioniert wenn ich explizit ein std::string Objekt (zB per Literal-Suffix) oder der MyStr-Klasse einen weiteren Konstruktor für const char* hinzufüge ist klar, gerade deswegen frage ich ja.



  • Der compiler macht nur genau eine "user defined conversion", d.h. nur eine Konvertierung, die nicht in die Sprache eingebaut ist, sondern Teil einer Klasse. std::string gehört zwar zur Standard-Bibliothek, aber nicht zum eigentlichen Sprachkern. Die Konvertierung char const* -> std::string zählt daher schon als user defined conversion und die zweite Konvertierung nach MyStr ist nicht mehr drin.

    Zur Sprache gehörige Konvertierungen sind vor und nach der user defined conversion erlaubt. Wenn ein char* an eine Funktion übergeben wird, die einen std::string const& erwartet, dann erfolgt folgende Konvertierungs-Reihe:

    • char* -> char const* ("built in")
    • char const* -> std::string ("user defined")

    Erst danach kann die Referenz an den String gebunden werden.



  • Ah, vielen Dank! Da merkt man wieder, dass selbst wenn man einige Jahre programmiert, man immer wieder Sachen dazulernt die man bisher nicht kannte 🙂
    Werde mir wenn ich Zeit habe, mehr über Implizite Konvertierungen lesen.

    Was ich mich jetzt allerdings Frage: Mit {"Test"} werden eben zwei "user defined conversions" durchgeführt, wieso ist das dann hier legal?


  • Mod

    pumuckl schrieb:

    std::string -> std::string const ("built in")

    Das ist kein Teil der Konvertierung. Die zweite SCS ist leer.

    Meine Frage wäre: Wieso ist das so und wieso funktioniert es erst mit den zusätzlichen Klammern?

    Man siehe [dcl.init.list]/3:

    Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary.

    D.h. DoSth({"Test"}); ist äquivalent zu

    MyStr s = {"Test"};
    DoSth(s);
    

    … was natürlich in Ordnung ist.



  • Arcoth schrieb:

    pumuckl schrieb:

    std::string -> std::string const ("built in")

    Das ist kein Teil der Konvertierung. Die zweite SCS ist leer.

    Danke, bin heute nicht ganz auf der Höhe. Hab's korrigiert 🙂


Log in to reply