In C++11 we can define copy and move constructors, but are both allowed on the same class? If so, how do you disambiguate their usage? For example:
Foo MoveAFoo() {
Foo f;
return f;
}
Is the above a copy? A move? How do I know?
Usually it will be neither due to RVO.
If that optimisation can't be performed, then it will be a move, because the object being returned is going out of scope (and will be destroyed just after). If it can't be moved, then it will be copied. If it can't be copied, it won't compile.
The whole point of move constructors is that when a copy is going to be made of an object that is just about to be destroyed, it is often unnecessary to make a whole copy, and the resources can be moved from the dying object to the object being created instead.
You can tell when either the copy or move constructor is going to be called based on what is about to happen to the object being moved/copied. Is it about to go out of scope and be destructed? If so, the move constructor will be called. If not, the copy constructor.
Naturally, this means you may have both a move constructor and copy constructor in the same class. You can also have a copy assignment operator and a move assignment operator as well.
Update: It may be unclear as to exactly when the move constructor/assignment operator is called versus the plain copy constructor/assignment operator. If I understand correctly, the move constructor is called if an object is initialised with an xvalue (eXpiring value). §3.10.1 of the standard says
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 ]
And the beginning of §5 of the standard says:
[ Note: An expression is an xvalue if it is:
the result of calling a
function, whether implicitly or explicitly, whose return type is an
rvalue reference to object type,
a cast to an rvalue reference to
object type,
a class member access expression designating a
non-static data member of non-reference type in which the object
expression is an xvalue, or
a .* pointer-to-member expression in
which the first operand is an xvalue and the second operand is a
pointer to data member.
In general, the effect of this rule is that
named rvalue references are treated as lvalues and unnamed rvalue
references to objects are treated as xvalues; rvalue references to
functions are treated as lvalues whether named or not. —end note ]
As an example, if NRVO can be done, it's like this:
void MoveAFoo(Foo* f) {
new (f) Foo;
}
Foo myfoo; // pretend this isn't default constructed
MoveAFoo(&myfoo);
If NRVO can't be done but Foo is moveable, then your example is a little like this:
void MoveAFoo(Foo* fparam) {
Foo f;
new (fparam) Foo(std::move(f));
}
Foo f; // pretend this isn't being default constructed
MoveAFoo(&f);
And if it can't be moved but it can be copied, then it's like this
void MoveAFoo(Foo* fparam) {
Foo f;
new (fparam) Foo((Foo&)f);
}
Foo f; // pretend this isn't default constructed
MoveAFoo(&f);
To back up #Seth, here's the relevant paragraph from the standard:
§12.8 [class.copy] p32
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, 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. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]
The "disambiguation" is just your old friend, overload resolution:
Foo y;
Foo x(y); // copy
Foo x(std::move(y)); // move
The expression y in the first example is an lvalue of type Foo, which binds to Foo const & (and also Foo & if you have such a constructor); the type of the expression std::move(y) in the second example is Foo &&, so it'll bind to Foo && (and also Foo const & absent the former).
In your example, the result of MoveAFoo() is a temporary of type Foo, so it'll bind to the Foo &&-constructor if one is available, and to a const-copy constructor otherwise.
Finally, in a function returning Foo (by value), the statement return x; is equivalent to return std::move(x); if x is a local variable of type Foo -- this is a special new rule to make the use of move semantics easier.
Foo MoveAFoo() {
Foo f;
return f;
}
This is definition of function MoveAFoo that returns object of type Foo. In its body local Foo f; is created and destructed when it goes out of its scope.
In this code:
Foo x = MoveAFoo();
object Foo f; is created inside of MoveAFoo function and directly assigned into x, which means that copy constructor is not called.
But in this code:
Foo x;
x = MoveAFoo();
object Foo f; is created inside of MoveAFoo function, then the copy of f is created and stored into x and original f is destructed.
Related
Why is the copy constructor called when returning from bar instead of the move constructor?
#include <iostream>
using namespace std;
class Alpha {
public:
Alpha() { cout << "ctor" << endl; }
Alpha(Alpha &) { cout << "copy ctor" << endl; }
Alpha(Alpha &&) { cout << "move ctor" << endl; }
Alpha &operator=(Alpha &) { cout << "copy asgn op" << endl; }
Alpha &operator=(Alpha &&) { cout << "move asgn op" << endl; }
};
Alpha foo(Alpha a) {
return a; // Move ctor is called (expected).
}
Alpha bar(Alpha &&a) {
return a; // Copy ctor is called (unexpected).
}
int main() {
Alpha a, b;
a = foo(a);
a = foo(Alpha());
a = bar(Alpha());
b = a;
return 0;
}
If bar does return move(a) then the behavior is as expected. I do not understand why a call to std::move is necessary given that foo calls the move constructor when returning.
There are 2 things to understand in this situation:
a in bar(Alpha &&a) is a named rvalue reference; therefore, treated as an lvalue.
a is still a reference.
Part 1
Since a in bar(Alpha &&a) is a named rvalue reference, its treated as an lvalue. The motivation behind treating named rvalue references as lvalues is to provide safety. Consider the following,
Alpha bar(Alpha &&a) {
baz(a);
qux(a);
return a;
}
If baz(a) considered a as an rvalue then it is free to call the move constructor and qux(a) may be invalid. The standard avoids this problem by treating named rvalue references as lvalues.
Part 2
Since a is still a reference (and may refer to an object outside of the scope of bar), bar calls the copy constructor when returning. The motivation behind this behavior is to provide safety.
References
SO Q&A - return by rvalue reference
Comment by Kerrek SB
yeah, very confusing. I would like to cite another SO post here implicite move. where I find the following comments a bit convincing,
And therefore, the standards committee decided that you had to be
explicit about the move for any named variable, regardless of its
reference type
Actually "&&" is already indicating let-go and at the time when you do "return", it is safe enough to do move.
probably it is just the choice from standard committee.
item 25 of "effective modern c++" by scott meyers, also summarized this, without giving much explanations.
Alpha foo() {
Alpha a
return a; // RVO by decent compiler
}
Alpha foo(Alpha a) {
return a; // implicit std::move by compiler
}
Alpha bar(Alpha &&a) {
return a; // Copy ctor due to lvalue
}
Alpha bar(Alpha &&a) {
return std:move(a); // has to be explicit by developer
}
This is a very very common mistake to make as people first learn about rvalue references. The basic problem is a confusion between type and value category.
int is a type. int& is a different type. int&& is yet another type. These are all different types.
lvalues and rvalues are things called value categories. Please check out the fantastic chart here: What are rvalues, lvalues, xvalues, glvalues, and prvalues?. You can see that in addition to lvalues and rvalues, we also have prvalues and glvalues and xvalues, and they form a various venn diagram sort of relation.
C++ has rules that say that variables of various types can bind to expressions. An expressions reference type however, is discarded (people often say that expressions do not have reference type). Instead, the expression have a value category, which determines which variables can bind to it.
Put another way: rvalue references and lvalue references are only directly relevant on the left hand of the assignment, the variable being created/bound. On the right side, we are talking about expressions and not variables, and rvalue/lvalue reference-ness is only relevant in the context of determining value category.
A very simple example to start with is simple looking at things of purely type int. A variable of type int as an expression, is an lvalue. However, an expression consisting of evaluating a function that returns an int, is an rvalue. This makes intuitive sense to most people; the key thing though is to separate out the type of an expression (even before references are discarded) and its value category.
What this is leading to, is that even though variables of type int&& can only bind to rvalues, does not mean that all expressions with type int&&, are rvalues. In fact, as the rules at http://en.cppreference.com/w/cpp/language/value_category say, any expression consisting of naming a variable, is always an lvalue, no matter the type.
That's why you need std::move in order to pass along rvalue references into subsequent functions that take by rvalue reference. It's because rvalue references do not bind to other rvalue references. They bind to rvalues. If you want to get the move constructor, you need to give it an rvalue to bind to, and a named rvalue reference is not an rvalue.
std::move is a function that returns an rvalue reference. And what's the value category of such an expression? An rvalue? Nope. It's an xvalue. Which is basically an rvalue, with some additional properties.
In both foo and bar, the expression a is an lvalue. The statement return a; means to initialize the return value object from the initializer a, and return that object.
The difference between the two cases is that overload resolution for this initialization is performed differently depending on whether or not a declared as a non-volatile automatic object within the innermost enclosing block, or a function parameter.
Which it is for foo but not bar. (In bar , a is declared as a reference). So return a; in foo selects the move constructor to initialize the return value, but return a; in bar selects the copy constructor.
The full text is C++14 [class.copy]/32:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration , and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]
where "criteria for elision of a copy/move operation are met" refers to [class.copy]/31.1:
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing
the automatic object directly into the function’s return value
Note, these texts will change for C++17.
If I understood correctly, starting from C++17, this code now requires that no copy will be done:
Foo myfunc(void) {
return Foo();
}
auto foo = myfunc(); // no copy
Is it also true for function arguments? Will copies be optimized away in the following code?
Foo myfunc(Foo foo) {
return foo;
}
auto foo = myfunc(Foo()); // will there be copies?
In C++17, prvalues ("anonymous temporaries") are no longer objects. Instead, they are instructions on how to construct an object.
They can instantiate a temporary from their construction instructions, but as there is no object there, there is no copy/move construction to elide.
Foo myfunc(Foo foo) {
return foo;
}
So here, the function argument foo is moved into the prvalue return value of myfunc. You can think of this conceptually as "myfunc returns instructions on how to make a Foo". If those instructions are "not used" by your program, a temporary is automatically instantiated and uses those instructions.
(The instructions, by the way, include time travel provisions; the time-point of construction remains inside the function itself!)
auto foo = myfunc(Foo());
So here, Foo() is a prvalue. It says "construct a Foo using the () constructor". It is then used to construct the argument of myfunc. No elision occurs, no copy constructor or move constructor is called, just ().
Stuff then happens inside myfunc.
myfunc returns a prvalue of type Foo. This prvalue (aka construction instructions) is used to construct the local variable auto foo.
So what happens here is that a Foo is constructed via (), then moved into auto foo.
Elision of function arguments into return values is not supported in C++14 nor C++17 as far as I know (I could be wrong, I do not have chapter and verse of the standard here). However, they are implicitly moved when used in a return func_arg; context.
Yes and no. Quoting cppreference:
Under the following circumstances, the compilers are required to omit the copy- and move- construction of class objects [...]:
In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object
In a function call, if the operand of a return statement is a prvalue and the return type of the function is the same as the type of that prvalue.
So, in your second snippet only one default constructor will be called. First, foo in myFunc is initialized from Foo() (1 default construction), which is a prvalue. This means that it will be elided (see point 1).
Next, myFunc returns a copy of foo, which cannot be elided as foo is not a prvalue (point 2). So, one move is made, as foo is a xvalue. But the actual return value is a prvalue, as it is a new instance of foo (in myFunc), and because of point one it is elided.
In conclusion, one default construction and one move is guaranteed by the Standard. There cannot be any more. But, the compiler might actually elide the only move altogether.
My understanding is that, in the following function, the expression foo in the statement return foo; is an xvalue, because the object it denotes is expiring (even though foo is an lvalue in previous statements):
Foo bar()
{
Foo foo;
change(foo);
return foo;
}
Such an expiring value is not covered by What expressions create xvalues?.
Does that change in the following case?
Foo bar(Foo&& foo)
{
change(foo);
return foo;
}
Is foo an xvalue in the return statement? And in particular, is it a candidate for move? And for RVO? Or should one use return std::move(foo)?
I do not know what the formal rule is for classifying the expression foo as an xvalue in the return statement of the first case, so I cannot test it in the second.
In that function, foo is an lvalue of type "rvalue reference to Foo". When you return it, since a copy has to be constructed (due to the type of the returned value), you are constructing a whole new value, which makes bar(...) an prvalue, as per §3.10.1.5:
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 ]
Due to the fact that, inside the function, foo is an lvalue, the expression return foo is not a candidate for a move construction and the copy constructor is selected.
And yes, RVO applies here, assuming a move is not selected first. There's nothing special in that regard here. As per §12.8.31:
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
[...]
To clarify, foo per se is an lvalue, but the statement:
return foo;
ultimately results (from the bar(...) expression) in a prvalue due to the fact that given that return type, the expression is equivalent to:
return Foo(foo);
which means that a temporary value, copied from foo is returned from the function bar.
Rereading the reply, it still does not make sense to me. You say Due to the fact that, inside the function, foo is an lvalue, the expression return foo is not a candidate for a move construction and the copy constructor is selected. Why is this true in one case and not the other?
When returning foo you have to create a new Foo value (because you are returning a copy) from the lvalue reference foo. This is done implicitly by the copy constructor. So return foo; is equivalent to return Foo(foo). Given that foo is an lvalue, the copy constructor is selected (and not the move constructor).
Now, when you have this new temporary value (constructed from foo), the value itself, which comes out of the expression bar(...), is a prvalue. So when you do:
auto res = bar(...);
You have to construct a Foo copy out of a prvalue. Since a prvalue is also an rvalue, the constructor with the rvalue reference parameter (move constructor) is selected.
Is it possible to write C++ code where we rely on the return value optimization (RVO) when possible, but fall back on move semantics when not? For example, the following code can not use the RVO due to the conditional, so it copies the result back:
#include <iostream>
struct Foo {
Foo() {
std::cout << "constructor" << std::endl;
}
Foo(Foo && x) {
std::cout << "move" << std::endl;
}
Foo(Foo const & x) {
std::cout << "copy" << std::endl;
}
~Foo() {
std::cout << "destructor" << std::endl;
}
};
Foo f(bool b) {
Foo x;
Foo y;
return b ? x : y;
}
int main() {
Foo x(f(true));
std::cout << "fin" << std::endl;
}
This yields
constructor
constructor
copy
destructor
destructor
fin
destructor
which makes sense. Now, I could force the move constructor to be called in the above code by changing the line
return b ? x : y;
to
return std::move(b ? x : y);
This gives the output
constructor
constructor
move
destructor
destructor
fin
destructor
However, I don't really like to call std::move directly.
Really, the issue is that I'm in a situation where I absolutely, positively, can not call the copy constructor even when the constructor exists. In my use case, there's too much memory to copy and although it'd be nice to just delete the copy constructor, it's not an option for a variety of reasons. At the same time, I'd like to return these objects from a function and would prefer to use the RVO. Now, I don't really want to have to remember all of the nuances of the RVO when coding and when it's applied an when it's not applied. Mostly, I want the object to be returned and I don't want the copy constructor called. Certainly, the RVO is better, but the move semantics are fine. Is there a way to the RVO when possible and the move semantics when not?
Edit 1
The following question helped me figure out what's going on. Basically, 12.8.32 of the standard states:
When the criteria for elision of a copy operation are met or would be
met save for the fact that the source object is a function parameter,
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. If overload resolution
fails, or if the type of the first parameter of the selected
constructor is not an rvalue reference to the object’s type (possibly
cv-qualified), overload resolution is performed again, considering the
object as an lvalue. [ Note: This two-stage overload resolution must
be performed regardless of whether copy elision will occur. It
determines the constructor to be called if elision is not performed,
and the selected constructor must be accessible even if the call is
elided. —end note ]
Alright, so to figure out what the criteria for a copy elison are, we look at 12.8.31
in a return statement in a function with a class return type, when the
expression is the name of a non-volatile automatic object (other than
a function or catch-clause parameter) with the same cvunqualified type
as the function return type, the copy/move operation can be omitted by
constructing the automatic object directly into the function’s return
value
As such, if we define the code for f as:
Foo f(bool b) {
Foo x;
Foo y;
if(b) return x;
return y;
}
Then, each of our return values is an automatic object, so 12.8.31 says that it qualifies for copy elison. That kicks over to 12.8.32 which says that the copy is performed as if it were an rvalue. Now, the RVO doesn't happen because we don't know a priori which path to take, but the move constructor is called due to the requirements in 12.8.32. Technically, one move constructor is avoided when copying into x. Basically, when running, we get:
constructor
constructor
move
destructor
destructor
fin
destructor
Turning off elide on constructors generates:
constructor
constructor
move
destructor
destructor
move
destructor
fin
destructor
Now, say we go back to
Foo f(bool b) {
Foo x;
Foo y;
return b ? x : y;
}
We have to look at the semantics for the conditional operator in 5.16.4
If the second and third operands are glvalues of the same value
category and have the same type, the result is of that type and value
category and it is a bit-field if the second or the third operand is a
bit-field, or if both are bit-fields.
Since both x and y are lvalues, the conditional operator is an lvalue, but not an automatic object. Therefore, 12.8.32 doesn't kick in and we treat the return value as an lvalue and not an rvalue. This requires that the copy constructor be called. Hence, we get
constructor
constructor
copy
destructor
destructor
fin
destructor
Now, since the conditional operator in this case is basically copying out the value category, that means that the code
Foo f(bool b) {
return b ? Foo() : Foo();
}
will return an rvalue because both branches of the conditional operator are rvalues. We see this with:
constructor
fin
destructor
If we turning off elide on constructors, we see the moves
constructor
move
destructor
move
destructor
fin
destructor
Basically, the idea is that if we return an rvalue we'll call the move constructor. If we return an lvalue, we'll call the copy constructor. When we return a non-volatile automatic object whose type matches that of the return type, we return an rvalue. If we have a decent compiler, these copies and moves may be elided with the RVO. However, at the very least, we know what constructor is called in case the RVO can't be applied.
When the expression in the return statement is a non-volatile automatic duration object, and not a function or catch-clause parameter, with the same cv-unqualified type as the function return type, the resulting copy/move is eligible for copy elision. The standard also goes on to say that, if the only reason copy elision was forbidden was that the source object was a function parameter, and if the compiler is unable to elide a copy, the overload resolution for the copy should be done as if the expression was an rvalue. Thus, it would prefer the move constructor.
OTOH, since you are using the ternary expression, none of the conditions hold and you are stuck with a regular copy. Changing your code to
if(b)
return x;
return y;
calls the move constructor.
Note that there is a distinction between RVO and copy elision - copy elision is what the standard allows, while RVO is a technique commonly used to elide copies in a subset of the cases where the standard allows copy elision.
Yes, there is. Don't return the result of a ternary operator; use if/else instead. When you return a local variable directly, move semantics are used when possible. However, in your case you're not returning a local directly -- you're returning the result of an expression.
If you change your function to read like this:
Foo f(bool b) {
Foo x;
Foo y;
if (b) { return x; }
return y;
}
Then you should note that your move constructor is called instead of your copy constructor.
If you stick to returning a single local value per return statement then move semantics will be used if supported by the type.
If you don't like this approach then I would suggest that you stick with std::move. You may not like it, but you have to pick your poison -- the language is the way that it is.
An lvalue is a value bound to a definitive region of memory whereas an rvalue is an expression value whose existence is temporary and who does not necessarily refer to a definitive region of memory. Whenever an lvalue is used in a position in which an rvalue is expected, the compiler performs an lvalue-to-rvalue conversion and then proceeds with evaluation.
http://www.eetimes.com/discussion/programming-pointers/4023341/Lvalues-and-Rvalues
Whenever we construct a temporary (anonymous) class object or return a temporary class object from a function, although the object is temporary, it is addressable. However, the object still is a valid rvalue. This means that the object is a) an addressable rvalue or b) is being implicitly converted from an lvalue to an rvalue when the compiler expects an lvalue to be used.
For instance:
class A
{
public:
int x;
A(int a) { x = a; std::cout << "int conversion ctor\n"; }
A(A&) { std::cout << "lvalue copy ctor\n"; }
A(A&&) { std::cout << "rvalue copy ctor\n"; }
};
A ret_a(A a)
{
return a;
}
int main(void)
{
&A(5); // A(5) is an addressable object
A&& rvalue = A(5); // A(5) is also an rvalue
}
We also know that temporary objects returned (in the following case a) by functions are lvalues as this code segment:
int main(void)
{
ret_a(A(5));
}
yields the following output:
int conversion ctor
lvalue copy ctor
Indicating that the call to the function ret_a using actual argument A(5) calls the conversion constructor A::A(int) which constructs the function's formal argument a with the value 5.
When the function completes execution, it then constructs a temporary A object using a as its argument, which invokes A::A(A&). However, if we were to remove A::A(A&) from the list of overloaded constructors, the returned temporary object would still match the rvalue-reference constructor A::A(A&&).
This is what I'm not quite understanding: how can the object a match both an rvalue reference and an lvalue reference? It is clear that A::A(A&) is a better match than A::A(A&&) (and therefore a must be an lvalue). But, because an rvalue reference cannot be initialized to an lvalue, given that the formal argument a is an lvalue, it should not be able to match the call to A::A(A&&). If the compiler is making an lvalue-to-rvalue conversion it would be trivial. The fact that a conversion from 'A' to 'A&' is also trivial, both functions should have identical implicit conversion sequence ranks and therefore, the compiler should not be able to deduce the best-matching function when both A::A(A&) and A::A(A&&) are in the overloaded function candidate set.
Moreover, the question (which I previously asked) is:
How can a given object match both an rvalue reference and an lvalue reference?
For me:
int main(void)
{
ret_a(A(5));
}
Yields:
int conversion ctor
rvalue copy ctor
(i.e. rvalue, not lvalue). This is a bug in your compiler. However it is understandable as the rules for this behavior changed only a few months ago (Nov. 2010). More on this below.
When the function completes execution,
it then constructs a temporary A
object using a as its argument, which
invokes A::A(A&).
Actually no. When the function ret_a completes execution, it then constructs a temporary A object using a as its argument, which invokes A:A(A&&). This is due to [class.copy]/p33]1:
When the criteria for elision of a
copy operation are met or would be met
save for the fact that the source
object is a function parameter, 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. If overload
resolution fails, or if the type of
the first parameter of the selected
constructor is not an rvalue reference
to the object’s type (possibly
cv-qualified), overload resolution is
performed again, considering the
object as an lvalue. [ Note: This
two-stage overload resolution must be
performed regardless of whether copy
elision will occur. It determines the
constructor to be called if elision is
not performed, and the selected
constructor must be accessible even if
the call is elided. — end note ]
However if you remove the A::A(A&&) constructor, then A::A(&) will be chosen for the return. Although in this case, then the construction of the argument a will fail because you can't construct it using an rvalue. However ignoring that for the moment, I believe your ultimate question is:
How can a given object match both an
rvalue reference and an lvalue
reference?
in referring to the statement:
return a;
And the answer is in the above quoted paragraph from the draft standard: First overload resolution is tried as if a is an rvalue. And if that fails, overload resolution is tried again using a as an lvalue. This two-stage process is tried only in the context wherein copy elision is permissible (such as a return statement).
The C++0x draft has just recently been changed to allow the two-stage overload resolution process when returning arguments that have been passed by value (as in your example). And that is the reason for the varying behavior from different compilers that we are seeing.