Search code examples
c++c++11move

Is there any reason to capture a return-value as an rvalue-reference?


I have a move-only struct Foo, and a function Foo get();

If I want to capture the return value of get in a mutable manner, I have two options:

  • by value (Foo)
  • by rvalue-reference (Foo&&)

When I capture by rvalue-reference I create an lvalue, much like capturing by value.

I'm struggling to see the point between the different options?

  • Is there any difference between these two?

Working example:

#include <iostream>

struct Foo
{
    Foo(std::string s) : s(std::move(s)) {}
    Foo(Foo&& f)       : s(std::move(f.s)) {}

    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;

    std::string s;
};

Foo get()
{
    return Foo { "hello" };
}

int main()
{
    // capture return value as l-value
    Foo lv1 = get();

    // move into another lvalue
    Foo lv2 = std::move(lv1);
    std::cout << lv2.s << '\n';

    // capture return value as r-value reference
    Foo&& rv1 = get();

    // move into another lvalue
    Foo lv3 = std::move(rv1);
    std::cout << lv3.s << '\n';

    return 0;
}

Solution

  • Foo lv1 = get();
    

    This requires that Foo is copy/moveable.

    Foo&& rv1 = get();
    

    This does not (at least, not as far as this line of code is concerned; the implementation of get may still require one).

    Even though compilers are permitted to elide the copy of the return value into the variable, copy initialization of this form still requires the existence of an accessible copy or move constructor.

    So if you want to impose as few limitations on the type Foo as possible, you can store a && of the returned value.

    Of course, C++17 changes this rule so that the first one doesn't need a copy/move constructor.