optionale const reference auf nullptr setzen funktioniert, aber warum?



  • Habe gerade versucht im Titel genanntes zu machen:

    void nullPointerReference(const SomeClass& foo = nullptr) {
    //...
    }
    

    und war überrascht das es ging!

    Dann nachgeforscht und festgestellt, dass ich in SomeClass KEINEN Constructor für std::nullptr_t habe:

    //Ist nicht im Code
    SomeClass::SomeClass(std::nullptr_t nullPointer) { //... }
    

    habe.

    Kompiliert ohne Warnung und Fehler
    Hab dann gefunden was passiert ist!

    Wisst ihr auch was es war? Und ist das so gewollt?



  • Deine Dummheit? Keine Ahnung.



  • Da ist der Wurm drin. Was passiert wenn man

    int main() {
       nullPointerReference()
    }
    

    aufruft?

    Da der Constructor

    SomeClass(std::nullptr_t nullPointer)
    

    nicht existiert, wird wohl ein andere Constructor aufgerufen, aber welcher könnte denn da passen?

    Nun ein Constructor der einen Pointer eines beliebigen typs akzeptiert könnte passen z.B.:

    SomeClass(int* pInt);
    SomeClass(AnohterClass* anotherPointer);
    //usw...
    

    Aber auch keiner dieser Signaturen befindet sich in SomeClass!


  • Mod

    nullptr lässt sich in beliebige Zeiger (auf Objekte oder Funktionen) oder in Zeiger auf Member oder in Zeiger auf Memberfunktionen oder auch in bool konvertieren. Dein gesuchter Konstruktor wird wohl entweder nullptr_t selbst oder eben eines dieser Konvertierungsergebnisse akzeptieren.

    P.S. Für Ich-sehe-was-was-du-nicht-siehst-Spiele gibt es bessere Foren.



  • camper schrieb:

    nullptr lässt sich [...] auch in bool konvertieren. [...]

    Das ist passiert und ich fand das nicht gerade intuitiv.


  • Mod

    Ufftata schrieb:

    camper schrieb:

    nullptr lässt sich [...] auch in bool konvertieren. [...]

    Das ist passiert und ich fand das nicht gerade intuitiv.

    Dann viel Spaß, wenn du demnächst überall if(pointer != nullptr) schreibst, anstatt if(pointer) .



  • SeppJ schrieb:

    Ufftata schrieb:

    camper schrieb:

    nullptr lässt sich [...] auch in bool konvertieren. [...]

    Das ist passiert und ich fand das nicht gerade intuitiv.

    Dann viel Spaß, wenn du demnächst überall if(pointer != nullptr) schreibst, anstatt if(pointer) .

    nullptr_t != T*



  • SeppJ schrieb:

    Dann viel Spaß, wenn du demnächst überall if(pointer != nullptr) schreibst, anstatt if(pointer) .

    Huch?
    if (pointer) wäre doch bloss ein Problem wenn typeof(pointer) == nullptr_t . Also vermutlich nur innerhalb von Templates, wieso sonst sollte man ne Variable vom Typ nullptr_t machen und dann explizit gucken ob sie denn null ist?
    Also schon nervig aber nicht MEGA-nervig.

    Und davon abgesehen, um es zu erlauben hätte auch ein expliziter operator bool gereicht ( if (expression) gilt ja als explizite Konvertierung nach bool).

    Bin also auch etwas überrascht dass hier implizit konvertiert wird.

    Oder steh ich grav vollkommen auf dem Schlauf 😕


  • Mod

    Was SeppJ wohl meinte: "Die Konvertierung von nullptr nach bool funktioniert weil nullptr in einen Zeiger konvertiert werden kann [das ist falsch, denn dann wäre die Konvertierung mehrdeutig], und die konvertieren nach bool . Wenn letzteres verboten wird, dann funktioniert if (ptr) nicht mehr."


  • Mod

    hustbaer schrieb:

    Und davon abgesehen, um es zu erlauben hätte auch ein expliziter operator bool gereicht ( if (expression) gilt ja als explizite Konvertierung nach bool).

    Bin also auch etwas überrascht dass hier implizit konvertiert wird.

    Ich nicht. Warum sollten sich null pointer values und pointer unterschiedlich benehmen, wenn ich sie einem bool zuweise? Gerade das wäre verwirrend.


  • Mod

    C++14 präzisiert, dass die Konvertierung nur im Kontext einer Direktinitialisierung zulässig ist (wobei die genaue Bedeutung dieser Regel auch nicht völlig glasklar zu sein scheint #1781).

    Kurzer Test mit g++ (5.4.0,6.3.0,7.1.0): verweigert die Konvertierung für -std=c++11 und -std=c++14 und -std=c++17
    clang 4.0.0: führt die Konvertierung durch für alle Versionen, warnt aber auch in allen Fällen.


  • Mod

    Hmm. Im Nachhinein macht es doch Sinn. Schließlich war einer der motivierenden Gründe overload resolution:

    void f(bool);
    void f(int*);
    
    f(nullptr); // call #2
    

    Implizite Konvertierung nach bool würde zumindest dieses Beispiel brechen.


  • Mod

    Arcoth schrieb:

    Hmm. Im Nachhinein macht es doch Sinn. Schließlich war einer der motivierenden Gründe overload resolution:

    void f(int);
    void f(int*);
    
    f(nullptr); // call #2
    

    Implizite Konvertierung nach bool würde alle arithmetischen Typen mit sich ziehen. In diesem Beispiel wäre das egal, weil die zweite SCS in der UCS für #1 schlechter wäre (Promotion statt Identität). Aber es wäre trotzdem nicht gewollt, dass nullptr das Verhalten aufweist.

    Eine Standardkonvertierungssequenz kann nur maximal eine "richtige" Konvertierung haben (der Kram, der gleich am Anfang des Kapitels (jetzt 7 Standard Conversions) steht).
    nullptr_t->bool->int geht nicht in einer einzigen Sequenz.


  • Mod

    Ja, ich hatte zu pragmatisch gedacht; nullptr_t ist keine Klasse. Allerdings hast du den Edit verpasst. :p


  • Mod

    Wenn mich mein Geächtnis nicht täuscht, war u.a. auch genau dieses Beispiel eines, um motivierend zu zeigen, warum nullptr_t als Klasse nicht funktioniert (um NULL zu ersetzen), und direkte Unterstützung durch den Sprachkern erforderlich ist. Wer Lust hat, kann sich ja das entsprechende Paper zu Gemüte führen, ist ja nur ein paar Klicks entfernt.


  • Mod

    camper schrieb:

    Wenn mich mein Geächtnis nicht täuscht, war u.a. auch genau dieses Beispiel eines, um motivierend zu zeigen, warum nullptr_t als Klasse nicht funktioniert (um NULL zu ersetzen), und direkte Unterstützung durch den Sprachkern erforderlich ist.

    Weil explizite Konvertierungsoperatoren nicht standardisiert waren? Oder weil ein Konvertierungsoperator-Template in overload resolution einem non-template Konverierungsoperator per se unterliegt? Ich frage mich, ob man letzteres durch ein Template mit default argument lösen könnte.


  • Mod

    Arcoth schrieb:

    camper schrieb:

    Wenn mich mein Geächtnis nicht täuscht, war u.a. auch genau dieses Beispiel eines, um motivierend zu zeigen, warum nullptr_t als Klasse nicht funktioniert (um NULL zu ersetzen), und direkte Unterstützung durch den Sprachkern erforderlich ist.

    Weil explizite Konvertierungsoperatoren nicht standardisiert waren? Oder weil ein Konvertierungsoperator-Template in overload resolution einem non-template Konverierungsoperator per se unterliegt? Ich frage mich, ob man letzteres durch ein Template mit default argument lösen könnte.

    u.a. Das Problem mit NULL wurde ja schon diskutiert, bevor C++ überhaupt standardisiert wurde.
    Nachdem ich nochmal die entsprechenden Teile rausgesucht habe (N2431 #654 N2656), muss ich feststellen, dass ich mich wahrscheinlich getäuscht habe. Eventuell habe ich das mit einer Diskussion über bool-Konvertierungen für Smartpointer verwechselt (bevor es explizite Konvertierungsoperatoren gab) - da war es so dass dieser unspecified-bool am Besten (die beste Variante von mehreren schlechten Lösungen, wohlgemerkt) ein Zeiger auf Member(funktion) ist, damit nicht so komische Sachen wie:

    smart_ptr p;
    p*42;
    

    möglich sind.



  • SeppJ schrieb:

    Dann viel Spaß, wenn du demnächst überall if(pointer != nullptr) schreibst, anstatt if(pointer) .

    Auch wenn das nicht deine Welt ist: Bin kein Freund von impliziten Typumwandlungen und schreibe so etwas tatsächlich meist explizit aus - obwohl mir
    die impliziten Umwandlungen bekannt sind und ich weiss dass es nicht nötig wäre. Vorteil: Im Zweifelsfall lässt sich direkt aus dem Code ablesen, dass die
    Umwandlung so gewollt war und sich jemand Gedanken darüber gemacht hat. Weniger schreiben zu müssen empfand ich beim Programmieren noch nie als
    großen Vorteil, da zumindest bei mir die meiste Zeit nicht fürs Tippen draufgeht 😉


Log in to reply