2016-01-08 2 views
2

Avoir une classe Widget avec des constructeurs par défaut et déplacer, je voudrais écrire une fonction:Comment passer des arguments rvalue et lvalue en C++?

  1. accepte les lvalues ​​et rvalues ​​de type Widget comme argument,
  2. interne « se déplace » de cet argument, par exemple, à une autre instance Widget.

Par exemple:

Widget w; 
auto p1 = pointer_from_lrvalue(Widget()); // rvalue argument 
auto p2 = pointer_from_lrvalue(w); // lvalue argument 
// w is guaranteed to be no longer used from now on 

pointer_from_lrvalue() pourrait ressembler à:

std::unique_ptr<Widget> pointer_from_lrvalue(Widget w) { 
    return std::unique_ptr<Widget>(new Widget(std::move(w))); 
} 

Une telle approche par valeur passant montre Andrei Alexandrescu dans sa fonction scopeGuard() [ErrorHandling-slides page 48]. Cependant, l'inconvénient de cette approche est qu'il nécessite un constructeur de copie de Widget en cas de passage d'un argument lvalue w (au moins avec g ++ 4.9.2).

J'ai été capable de trouver les solutions suivantes pour empêcher l'appel/nécessitant un constructeur de copie. La première utilise des références lvalue et rvalue:

std::unique_ptr<Widget> pointer_from_lrvalue(Widget& w) { 
    return std::unique_ptr<Widget>(new Widget(std::move(w))); 
}  
std::unique_ptr<Widget> pointer_from_lrvalue(Widget&& w) { 
    return std::unique_ptr<Widget>(new Widget(std::move(w))); 
}  

Le second utilise une référence universelle:

template <typename T> 
std::unique_ptr<std::remove_reference_t<T>> pointer_from_lrvalue(T&& t) { 
    return std::unique_ptr<std::remove_reference_t<T>> 
     (new std::remove_reference_t<T>(std::move(t))); 
} 

Je me demande:

  1. Que ces solutions sont correctes et sont garantis pas appelez le constructeur de copie Widget?
  2. Laquelle de ces deux solutions devrais-je préférer?
  3. Existe-t-il une autre solution, peut-être meilleure, à ce problème?

Répondre

4
  1. Oui, ils sont à la fois correct et garanti à seulement invoquer mouvements.

  2. Ceci est plutôt subjectif. Personnellement, je préfère la référence de transfert, car je déteste la duplication de code. Cependant, cela nécessite de placer le code dans un en-tête.

  3. Si vous voulez aller de fantaisie, vous pouvez créer votre propre non-modèle « référence Widget mobile: »

    class WidgetMover 
    { 
        Widget& widget; 
    
    public: 
        Widget&& moveWidget() { return std::move(widget); } 
    
        WidgetMover(Widget &w) : widget(w) {} 
        WidgetMover(Widget &&w) : widget(w) {} 
    }; 
    
    std::unique_ptr<Widget> pointer_from_lrvalue(WidgetMover w) { 
        return std::unique_ptr<Widget>(new Widget(w.moveWidget())); 
    } 
    

    Il faut prendre soin de veiller à ce qu'aucun WidgetMover vie au-delà de la durée de vie d'un rvalue il aurait pu être initialisé avec.