Umgehung des const-Referenz-Status von Temporaries



  • Ich habe etwa folgenden Code, und der compiliert auch:

    class xyz {
      xyz();
    
      xyz(xyz &);
    
      operator xyz&() {
        return *this;
      }
    };
    
    ...
    
    void foo(xyz &);
    
    ...
    
    foo(xyz());
    

    Ist das legal?

    Bzw. warum sollte ich das nicht machen?



  • Hi,

    da steht alles dazu 😉 :
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-39474.html

    Edit: Ups, hätte wohl nicht den Code so überfliegen sollen, trotzdem ist der Link gut 😉 ...


  • Mod

    Mr. N schrieb:

    Ist das legal?

    Nein, allerdings nur, weil Konvertierungsoperatoren nicht implizit für Konvertierungen in die selbe Klasse oder eine Basisklasse aufgerufen werden [12.3.2/1 S.6] (sollen, msvc hält sich z.B. nicht daran).

    Was möglich ist, wäre z.B.

    struct foo
    {
        foo& self() {return *this;}
    };
    void bar(foo&);
    int main()
    {
        bar(foo().self());
    }
    

    Daran ist nichts verbotenes - sofern du einen guten Grund hast, ist daran nichts auszusetzen - beachte aber, dass das Temporary in jedem Falle am Ende des Ausdrucks zerstört wird. Also z.B. auch dann:

    int main()
    {
        const foo& x = foo().self();
        foo y = x; // ups, x verweist auf ein bereits zerstörtes Objekt
    }
    

    ein interessanteres Beispiel wäre:

    struct x { x(int); };
    ...
    x& y = x() = 1;
    x z = y; // boom
    


  • camper schrieb:

    Mr. N schrieb:

    Ist das legal?

    Nein, allerdings nur, weil Konvertierungsoperatoren nicht implizit für Konvertierungen in die selbe Klasse oder eine Basisklasse aufgerufen werden [12.3.2/1 S.6] (sollen, msvc hält sich z.B. nicht daran).

    GCC auch nicht. Benutzt auto_ptr deshalb diese merkwürdige auto_ptr_ref-Klasse?

    camper schrieb:

    Was möglich ist, wäre z.B.

    class foo
    {
    public:
        foo& self();
    };
    void bar(foo&);
    int main()
    {
        bar(foo().self());
    }
    

    Daran ist nichts verbotenes - sofern du einen guten Grund hast, ist daran nichts auszusetzen - beachte aber, dass das Temporary in jedem Falle am Ende des Ausdrucks zerstört wird. Also z.B. auch dann:

    int main()
    {
        const foo& x = foo().self();
        foo y = x; // ups, x verweist auf ein bereits zerstörtes Objekt
    }
    

    Das mit dem self() ist ziemlich umständlich.

    Vielleicht nehme ich die Illegalität einfach in Kauf.

    Aber es gibt noch einen Punkt, auf den ich anderswo hingewiesen wurde: Ein Temporary als Non-Const-Referenz zu behandeln sei UB.

    KasF schrieb:

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-39474.html

    Was hat das mit meinem Problem zu tun oder versteh ich den Thread nur nicht?



  • Mr. N schrieb:

    Was hat das mit meinem Problem zu tun oder versteh ich den Thread nur nicht?

    Sry. Siehe mein Edit. Habe einfach blind draufgeschaut und dachte es geht nur um das binden von temporären Objekten an Referenzparametern ....



  • KasF schrieb:

    Mr. N schrieb:

    Was hat das mit meinem Problem zu tun oder versteh ich den Thread nur nicht?

    Sry. Siehe mein Edit. Habe einfach blind draufgeschaut und dachte es geht nur um das binden von temporären Objekten an Referenzparametern ....

    Was mich irritiert ist, dass gleich der erste Code in dem Thread nicht compiliert.

    <stdin>:9: error: invalid initialization of non-const reference of type 'X&' from a temporary of type 'X'
    

  • Mod

    Der Thread ist alt. Vermutlich war das noch mit vc6 gemacht. vc hat eine Erweiterung, die es erlaubt, rvalues auch an non-const Referenzen zu binden.



  • Muss ich wirklich über self() gehen? Gibt es nicht eine weniger aufdringliche Variante? Wie problematisch wäre es, einfach auf den verbreiteten Compiler-Bug zu vertrauen, dass mein Code geht? (GCC 4.3 hat keine Probleme mit ihm.)



  • Undefined Behaviour in Kauf nehmen!?
    Was kommt danach? Gutheißen von Angriffskriegen und Rechtsextremismus?



  • kenner der dummköpfe schrieb:

    Undefined Behaviour in Kauf nehmen!?
    Was kommt danach? Gutheißen von Angriffskriegen und Rechtsextremismus?

    😃 👍



  • Mr. N schrieb:

    Muss ich wirklich über self() gehen? Gibt es nicht eine weniger aufdringliche Variante? Wie problematisch wäre es, einfach auf den verbreiteten Compiler-Bug zu vertrauen, dass mein Code geht? (GCC 4.3 hat keine Probleme mit ihm.)

    Wenns portabel sein soll schon.
    Für MSVC alleine brauchst du IIRC nichtmal den "operator xyz&", das ist ne "non-std-extension" (MSVC verweigert es auch wenn man die "non-std-extension" ausschaltet).



  • reicht dir vielleicht sowas?

    template<class T> struct ConstHelper
    {
       typedef int type;
    };
    
    template<class T> struct ConstHelper<T const>
    {
       template<class U> struct error {};
       typedef typename error<T>::type type;
    };
    
    template<class T>
    T& mutate_safely(T const& x)
    {
       return const_cast<T&>(x);
    }
    
    template<class T>
    T& mutate_safely(T& x, typename ConstHelper<T>::type = 0)
    {
       return x;
    }
    //...
    void foo (int&);
    foo (mutate_safely(42));
    

  • Mod

    queer_boy schrieb:

    reicht dir vielleicht sowas?

    template<class T> struct ConstHelper
    {
       typedef int type;
    };
    
    template<class T> struct ConstHelper<T const>
    {
       template<class U> struct error {};
       typedef typename error<T>::type type;
    };
    
    template<class T>
    T& mutate_safely(T const& x)
    {
       return const_cast<T&>(x);
    }
    
    template<class T>
    T& mutate_safely(T& x, typename ConstHelper<T>::type = 0)
    {
       return x;
    }
    //...
    void foo (int&);
    foo (mutate_safely(42));
    

    Das ist auch nur undefiniert auf einem höheren Level. Bei dieser Mine verlierst du nicht bloß den Fuß sondern gleich das ganze Bein. Nebenbei bemerkt, werden auch const-lvalues von der ersten Überladung konsumiert und nicht von der zweiten (1. weil diese spezieller ist und 2. weil die zweite Überladung dann dank SFINAE herausfällt).



  • Ich hab mal einen Bug gegen GCC berichtet:

    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=32658


Anmelden zum Antworten