boost::optional Falle



  • #include <iostream>
    
    #include <boost/optional.hpp>
    
    struct A
    {
    
    };
    
    int main()
    {
        boost::optional <A> opt;
        opt = {}; // nope!
        if (opt)
            std::cout << "is initialized?\n";
        opt = A{};
        if (opt)
            std::cout << "is initialized now!\n";
        return 0;
    }
    

    Ich bin gerade in eine kleine Falle getapt mit boost::optional.
    Die Zeile

    opt = {};
    

    führt nicht die erwartete intialisierung durch. Welcher Operator aufgerufen wird, weiß nicht, aber ich finde das Verhalten unzufriedenstellend.
    Welcher operator= wird hier aufgerufen, und stimmt ihr mir zu, dass das ekliges Verhalten ist (fixbar?)?



  • Naja, das scheint mir schon logisch. In dem einen Fall wird halt ein leeres Optional konstruiert und indem anderen Fall ein A default konstruiert und in das Optional reingeschoben.



  • Interessante Frage!

    Dein Code kompiliert hier gar nicht.

    Laut Dokumentation hat optional folgende Überladungen für den Zuweisungsoperator:

    optional& operator = ( none_t ) noexcept ;                           // 1
      optional& operator = ( T const& v ) ;                                // 2
      optional& operator = ( T&& v ) ;                                     // 3
      optional& operator = ( optional const& rhs ) ;                       // 4
      optional& operator = ( optional&& rhs ) noexcept(see below) ;        // 5
      template<class U> optional& operator = ( optional<U> const& rhs ) ;  // 6
      template<class U> optional& operator = ( optional<U>&& rhs ) ;       // 7
    

    Die Templates fallen bestimmt raus, weil man anhand von {} den Parameter U nicht deduzieren kann. Alles andere müsste "viable" sein, wobei 3 gegenüber 2 und 5 gegenüber 4 präferiert werden müsste. Allerdings kenne ich jetzt keine Regeln, die dafür verantwortlich seien, dass der Compiler sich zwischen 1,3,5 entscheiden könnte. Von daher sehe ich das ein, dass er meckert ("call to overloaded function is ambiguous"). Die Überladung 1 taucht in der Fehlermeldung jetzt nicht auf. Das hieße, dass man ein none_t nicht mit {} initialisiert bekommt. OK. *achselzuck*

    Überladung in Verbindung mit {} würde ich echt vermeiden. Beispielsweise compiliert das hier aus irgendeinem Grund: https://ideone.com/bMBzJ7 Keine Ahnung, warum int hier präferiert wird. Auch wenn ich foo einem Initializer-List-Constructor spendiere, ändert das nichts. 😕



  • krümelkacker schrieb:

    Interessante Frage!

    Dein Code kompiliert hier gar nicht.

    😮

    Das wäre auf jeden Fall IMHO besser, als das hier.
    (soweit ich weiß habe ich gcc 6.3 und die neuste boost version verwendet.)

    Ich benutze {} oft für "hier erzeug mal das richtige objekt für mich".

    EDIT: boost::none sollte sich auf jeden fall nicht so konstruieren lassen.



  • Skym0sh0 schrieb:

    Naja, das scheint mir schon logisch. In dem einen Fall wird halt ein leeres Optional konstruiert und indem anderen Fall ein A default konstruiert und in das Optional reingeschoben.

    Das "richtige" Verhalten, zumindest von std::optional, wäre aber das Objekt zu deinitialisieren. Macht boost ab 1.63 nun auch.

    EDIT: http://www.boost.org/users/history/version_1_63_0.html (nach optional scrollen)


  • Mod

    krümelkacker schrieb:

    Überladung in Verbindung mit {} würde ich echt vermeiden. Beispielsweise compiliert das hier aus irgendeinem Grund: https://ideone.com/bMBzJ7 Keine Ahnung, warum int hier präferiert wird.

    Was verwirrt dich hier? In einem Fall muss doch ein kompletter Konstruktor aufgerufen werden, im anderen nicht. Daher macht [over.ics.list]/6 aus ersterem eine user-defined conversion, und …/9.2 aus letzterem eine identity conversion.

    Auch wenn ich foo einem Initializer-List-Constructor spendiere, ändert das nichts. 😕

    Natürlich nicht, das gleiche legale und intuitive Argument greift.


Log in to reply