-1

In the following example, why doesn't the move constructor get called in the construction of 'copy' inside fun, even though the 'src' argument of 'fun' is explicitly a rvalue reference and is only used in that construction?

struct Toy {
    int data;

    Toy(): data(0)
    {
        log("Constructed");
    }

    Toy(Toy const& src): data(src.data)
    {
        log("Copy-constructed");
    }

    Toy(Toy&& src): data(src.data)
    {
        log("Move-constructed");
    }
};

Toy fun(Toy&& src)
{
    Toy copy(src);
    copy.data = 777;
    return copy;
}

Toy toy(fun(Toy())); // LOG: Constructed Copy-constructed
user3026691
  • 497
  • 2
  • 11
  • 1
    possible duplicate of [What are copy elision and return value optimization?](http://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) – nosid May 11 '14 at 13:35
  • 1
    by the way your example is neither complete or usable, next time post something that others can use, not just random snippets that appears to replicate your problem from your prospective . – user2485710 May 11 '14 at 13:37
  • I don't understand how copy elision or return value optimization have anything to do with the way the 'copy' object is constructed inside the function. What happens is clearly counter-intuitive and counter-productive. – user3026691 May 11 '14 at 13:53
  • The code comment suggests that the move constructor never gets called. The question mentions the 'src' argument being an rvalue reference and being used to construct the 'copy' object inside 'fun', which I assumed would be enough for the bright minds here to understand where I expected move construction to happen. -_- – user3026691 May 11 '14 at 14:39

1 Answers1

0

While Bob && b is an rvalue reference, all named use of data after construction is using it as an lvalue.

So Bob&& b will only bind to rvalues, but when you use it it will not move.

The only ways to get an rvalue reference are:

  • A value without a name, such as a temporary return value or result of a cast.

  • Use of a local value variable in a simple return x; statement.

  • Explicitly casting to an rvalue, such as with std::move or std::forward.

This prevents data from being silently moved from on one line and then used on the next. It can help to think of rvalue as being 'I the programmer say this is not needed after this expression' at use, and 'only take things that are not needed afterwards' in function parameters. The temporary/return exceptions above are two spots the compiler can relatively safely guarantee this itself.

Finally, note that universal references (auto&& and T&&) look like rvalue references but sometimes are not.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I don't quite understand... Toy(), as passed to fun, is a value without a name, fun recognizes it as "a thing that is not needed afterwards" and indeed, it's never used except to construct another object, so why can't the compiler perform a move? – user3026691 May 11 '14 at 14:55
  • @user3026691 in the method it has a name - `src` - so it is an lvalue on use. It is not cast to an rvalue, nor is it a local value being used in `return x;`, so the case is clear that it is an lvalue at point of use. The compiler follows the rules of C++11, which say 'lvalue'. To fix, `std::move`. I am not going to discuss why C++11 says 'lvalue' in this case, as it is irrelevant to your problem. – Yakk - Adam Nevraumont May 11 '14 at 15:15