std::forward oder std::move: Ich komm durcheinander



  • Hallo.

    smart_union(T&& value){
            set_first(std::move(value));
        }
    

    Für set_first gibt es zwei Funktionen. Einmal set_first(const T& ) und einmal set_first(T&& ). Muss ich nun bei set_first() std::move() oder std::forward() machen?



  • Du kannst es dir so merken: move ist ein Cast, der dir immer eine Rvalue-Referenz liefert. forward ist ein bedingter Cast, der Lvalue Lvalue und Rvalue Rvalue sein lässt. In deinem Fall ist vermutlich forward die richtige Wahl, da es sich bei value wohl um eine Universal Reference handelt.



  • Jodocus schrieb:

    In deinem Fall ist vermutlich forward die richtige Wahl, da es sich bei value wohl um eine Universal Reference handelt.

    Hm, bist du dir sicher? Klar, es ist ein

    template<typename T, typename U>
    class smart_union { ... };
    

    aber sowas wie

    smart_union<int&, std::string> inst;
    

    wird nicht vorkommen können.

    Und ich hab auch zwei Versionen des Konstruktors. Einmal konstante Referenz, einmal für die Move Semantics.

    Und ich hab folgendes getestet:

    int&& x = 10;
    int&& y = std::move(x); // std::move() muss man explizit mit angeben, sonst kompilierts nicht
    

    Und das funktioniert einwandfrei und ruft auch keinen Konstruktor auf.

    Also denke ich wohl eher, dass std::move() doch die richtige Wahl war?
    Oder was versteh ich falsch?



  • Hallo! schrieb:

    Jodocus schrieb:

    In deinem Fall ist vermutlich forward die richtige Wahl, da es sich bei value wohl um eine Universal Reference handelt.

    Hm, bist du dir sicher?

    Nö, ich kenne deinen Code ja nicht. 😉 In deinem Fall würde ich evtl. statt den Konstruktor zu überladen ihm selbst zu einem Template mit Universal-Reference machen und dann perfect forwarden:

    template<typename T>
    struct foo {
        template <typename U> foo(U&& param) : set_first(std::forward<U>(param)) { }
        ...
    };
    




  • Ok, danke euch.

    Wenn ich das also richtig verstanden habe, gibt es universal references und rvalue references. Und universal references zu benutzen resultiert in weniger Code schreiben, als wie von mir gepostet. Falsch war mein Beispiel also nicht.

    Nadann benutz ich Universal references!



  • Moment mal, kurz ein anderes Thema. Warum kompiliert das nicht, wenn man eine Klasse instanzieren möchte:

    template<typename X, typename = typename std::enable_if<std::is_same<typename std::decay<X>::type, T>::value>::type>
        smart_union(X&& value){
            set_first(std::forward<X>(value));
        }
    
        template<typename X, typename = typename std::enable_if<std::is_same<typename std::decay<X>::type, U>::value>::type>
        smart_union(X&& value){
            set_second(std::forward<X>(value));
        }
    

    Für die Konstruktoren zumindest muss ich wohl die andere Variante beibehalten.



  • Nein, warte!

    T&& und U&& sind doch IMHO schon universal references. Die weiteren Template-Parameter sind unnütz.

    Ich glaub ich habs verstanden.



  • Ah, nein, es sind keine Universal references. Aber warum nicht?

    Scott Meyers schrieb:

    If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.

    T und U sind doch zwei Template Parameter die deduktiert werden, oder?


Log in to reply