有人可以解释为什么B不编译,但C会吗?我不明白为什么std :: move是必需的,因为变量已经是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?
};
答案 0 :(得分:22)
声明中:
B(B&& b)
参数 b
的声明类型为:B
的右值引用。
声明中:
A(b)
表达式 b
是B
类型的左值。
并且左值表达式不能绑定到右值引用:特别是语句中的右值引用:
A(A&& a)
这种逻辑完全遵循该语言的其他部分。考虑这个功能:
void
f(B& b1, B b2, B&& b3)
{
g(b1);
g(b2);
g(b3);
}
即使f
的参数都是使用不同类型声明的,表达式b1
,b2
和b3
都是B
类型的左值表达式,无论g
如何重载,都会调用相同的函数g
。
在C ++ 11中,区分变量的声明和使用该变量产生的表达式比以往任何时候都更加重要。并且表达式从不具有引用类型。相反,它们的值类别恰好是以下之一:lvalue,xvalue,prvalue。
声明:
A(std::move(c))
没问题,因为std::move
会返回右值引用。返回rvalue引用的函数调用产生的表达式具有值category:xvalue。与prvalues一起,xvalues被认为是rvalues。以及C
类型的右值表达式:
std::move(c)
将绑定到A(A&& a)
中的rvalue引用参数。
我发现下图(最初由Bjarne Stroustrup发明)非常有帮助:
expression
/ \
glvalue rvalue
/ \ / \
lvalue xvalue prvalue
答案 1 :(得分:12)
因为命名变量不是右值,即使声明&&也是如此。如果它有一个名称,那么它不是临时的,因此你需要使用std::move
。