3

In an Stack Overflow answer here, Kerrek posts the following code.

Foo && g()
{
    Foo y;
    // return y;         // error: cannot bind ‘Foo’ lvalue to ‘Foo&&’
    return std::move(y); // OK type-wise (but undefined behaviour, thanks @GMNG)
}

GManNickG points out that this results in undefined behavior.

Kerrek adds

True; you don't really return && from anything but forward and move. It's a contrived example.

What confuses me is that the C++11 standard uses a function call that returns an rvalue reference as an example of an expression that is an xvalue.

An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue reference is an xvalue. —end example ]

So when exactly does returning an rvalue reference result in undefined behavior? Does it always result in undefined behavior except for std::move and std::forward and is the standard just being terse? Or would you have to access the return value for undefined behavior to result?

*By "when" I mean "under what circumstances". I realize that there's no meaningful concept of time here.

Community
  • 1
  • 1
Praxeolitic
  • 22,455
  • 16
  • 75
  • 126
  • Trying to operate on an object that has been destroyed causes undefined behaviour. There's nothing special about an rvalue reference in this context. In this code `y` is destroyed as the function returns, before the calling code gets to look at the returned reference. – M.M Oct 12 '14 at 03:03
  • What's not clear to me is whether the code shown somehow in and of itself contains UB or whether accessing the return value is required for UB. – Praxeolitic Oct 12 '14 at 03:05
  • OK, I might have misread the question. Did you mean *at which point in the following code does UB occur*? – M.M Oct 12 '14 at 03:07
  • Almost. If the function is called but the return value is never accessed does UB occur at all? – Praxeolitic Oct 12 '14 at 03:09
  • 1
    The section on references says that a reference must be bound to a valid object. I'm not sure if the return value binding occurs before or after the object's destruction though. – M.M Oct 12 '14 at 03:18

1 Answers1

8

Casting into a reference does not extend the lifetime of the local variable.

As such, using that reference to the local after the local expires is undefined behaviour.

As the local expires when the function completes, there is no way to use the return value.

std::move or std::forward takes a reference, and returns a reference of possibly different type. Neither extend the lifetime of their arguement, and neither return a reference to a variable local to their own body.

The UB here is not returning an rvalue reference, but rather object lifetime based. It occurs on use, not on cast or return.

That said, it is almost never a good idea to return an rvalue reference: return a copy instead. Copies can particiate in lifetime extension. The exception is when you are writing something like forward_as_tuple.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524