Search code examples
c++referencemovelvaluervalue

How to move from both rvalue and lvalue arguments in C++?


Having a class Widget with default and move constructors, I'd like to write a function that:

  1. accepts both lvalues and rvalues of type Widget as an argument,
  2. internally 'moves' from this argument, e.g., to some other Widget instance.

For example:

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

where pointer_from_lrvalue() might look like:

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

Such a passing-by-value approach shows Andrei Alexandrescu in his scopeGuard() function [ErrorHandling-slides, page 48]. However, the drawback of this approach is that it requires a copy constructor of Widget in case of passing an lvalue argument w (at least with g++ 4.9.2).

I was able to find the following solutions to prevent calling/requiring a copy constructor. The first one uses lvalue and rvalue references:

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)));
}    

The second one uses a universal reference:

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)));
}

I wonder:

  1. Whether these solutions are correct and are guaranteed not to call the Widget copy constructor?
  2. Which of these two solutions should I prefer?
  3. Is there another, possibly better, solution to this problem?

Solution

    1. Yes, they're both correct and guaranteed to only invoke moves.

    2. This is rather subjective. I would personally prefer the forwarding reference, because I hate code duplication. However, it does require putting the code in a header.

    3. If you want to go fancy, you can create your own non-template "movable Widget reference:"

      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()));
      }
      

      Care must be taken to ensure that no WidgetMover lives beyond the lifetime of an rvalue it might have been initialised with.