0

According to what I know it is not valid to bind a lvalue into a rvalue reference. And secondly, a lvalue expression is recognizable by the fact it can be prefix by adress-of operator, (&)

I'm a little bit in trouble if this two sentences arecorrect with the following codes :

 #include<iostream>

struct Foo
{
    Foo(Foo&& other)
    {
        std::cout << "move ctor called";
    }

    Foo(const Foo& other)
    {
        std::cout << "copy ctor called";
    }

    Foo(){}
};

Foo return_foo()
{
    Foo f;
    return f;
}


void main()
{  
    Foo f = return_foo(); // Move ctor is called, but return_foo() is a lvalue ?? 
    std::cin.ignore();    
}

Where I am wrong ?

Guillaume07
  • 9,618
  • 13
  • 57
  • 130

2 Answers2

4

return_foo() returns a prvalue (because it returns unnamed temporary object). Quote from §3.10/1, emphasis mine:

A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [ Example: The result of calling a function whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is also a prvalue. —end example ]

Vitus
  • 11,413
  • 7
  • 32
  • 62
  • so we can put & before all lvalue expressions AND before all rvalue expressions which are not xvalue – Guillaume07 Jul 28 '11 at 20:43
  • If you mean `&` as address-of operator, then no. `&` is built-in operator that only works with lvalues. It can however be used with class types that overload this operator inside class (i.e. as member function), because you _can_ call member functions on temporary objects (rvalues). – Vitus Jul 28 '11 at 21:10
  • §5.3.1/3: The result of the unary `&` operator is a pointer to its operand. **The operand shall be an lvalue** or a qualified-id (qualified-id is used for pointer-to-member). If your compiler allows it, it's non-standard extension (for example, my gcc 4.7 allows it with -fpermissive). – Vitus Jul 28 '11 at 21:47
  • sorry to insist but to be clear: 1) return_foo is prvalue. 2) & must be used only use with lvalue 2) i can write &return_foo() => there is a incoherence, execpt if prvalue is a lvalue, but i don't think it is the case – Guillaume07 Jul 29 '11 at 05:44
  • Because you can write `&return_foo()` and have it compiled without errors **does not** mean it's in accordance with standard. And yes, prvalue is not lvalue (you can check [this graph](http://stackoverflow.com/questions/3601602#3601748) ). – Vitus Jul 29 '11 at 12:49
2

There is a special rule which allows returning a temporary as an rvalue, namely, the following are equivalent - the explicit "I don't need this anymore" version:

T foo()
{
  T t(a, b, ...); // constructed somehow
  /* ... */
  return std::move(t);
}

int main()
{
  T t = foo(); // we can move-construct this
}

... and the implicit version:

T foo()
{
  T t(a, b, ...);
  /* ... */
  return t;  // implicitly allow moving
}

All this happens after return-value optimization. This means that returning by value is actually pretty efficient in many situations.

Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
  • In your example, `t` is actually function type, I suppose you wanted to write `T t` or `T t{}`. Nitpicking aside, relevant parts of the standard are §12.8/31 and §12.8/32. - When the criteria for elision of a copy operation are met (...), and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. – Vitus Jul 28 '11 at 22:37
  • @Vitus: Thanks, fixed - I was going to provide some dummy arguments but then forgot. Fixed! Thanks also for the relevant quote! – Kerrek SB Jul 28 '11 at 22:39
  • To clarify -- the special rule is here to potentially optimize the copy of `t` into a move (while still allowing NRVO). But that copy or move conceptually takes place *inside* the function. `foo()` will always be a prvalue regardless of what's the actual `return` expression and whether a copy or a move or NRVO happened, because its type is `T()`. – Luc Danton Jul 29 '11 at 06:19