Reputation: 2055
Can someone explain why B doesn't compile, but C does? I don't understand why std::move is required since the variable is already an rvalue ref.
struct A {
int x;
A(int x=0) : x(x) {}
A(A&& a) : x(a.x) { a.x = 0; }
};
struct B : public A {
B() {}
B(B&& b) : A(b) {} // compile error with g++-4.7
};
struct C : public A {
C() {}
C(C&& c) : A(std::move(c)) {} // ok, but why?
};
Upvotes: 4
Views: 754
Reputation: 218750
In the statement:
B(B&& b)
The parameter b
is declared with the type: rvalue reference to B
.
In the statement:
A(b)
The expression b
is an lvalue of type B
.
And lvalue expressions can not bind to rvalue references: specifically the rvalue reference in the statement:
A(A&& a)
This logic follows cleanly from other parts of the language. Consider this function:
void
f(B& b1, B b2, B&& b3)
{
g(b1);
g(b2);
g(b3);
}
Even though the parameters of f
are all declared with different types, the expressions b1
, b2
and b3
are all lvalue expressions of type B
, and thus would all call the same function g
, no matter how g
is overloaded.
In C++11 it is more important than ever to distinguish between a variable's declaration, and the expression that results from using that variable. And expressions never have reference type. Instead they have a value category of precisely one of: lvalue, xvalue, prvalue.
The statement:
A(std::move(c))
is ok, because std::move
returns an rvalue reference. The expression resulting from a function call returning an rvalue reference has value category: xvalue. And together with prvalues, xvalues are considered rvalues. And the rvalue expression of type C
:
std::move(c)
will bind to the rvalue reference parameter in: A(A&& a)
.
I find the following diagram (originally invented by Bjarne Stroustrup) very helpful:
expression
/ \
glvalue rvalue
/ \ / \
lvalue xvalue prvalue
Upvotes: 25
Reputation: 81349
Because named variables are not rvalues, even when declared &&. If it has a name then its not temporary, thus you need to use std::move
.
Upvotes: 14