3

有人可以解释为什么 B 不能编译,但 C 可以吗?我不明白为什么需要 std::move ,因为变量已经是右值引用。

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?
};
4

2 回答 2

24

在声明中:

B(B&& b)

参数 b使用类型声明: rvalue reference to B

在声明中:

A(b)

表达式 b是类型的左值B

并且左值表达式不能绑定到右值引用:特别是语句中的右值引用:

A(A&& a)

这个逻辑完全遵循语言的其他部分。考虑这个函数:

void
f(B& b1, B b2, B&& b3)
{
   g(b1);
   g(b2);
   g(b3);
}

尽管 的参数f都声明为不同的类型,但表达式b1和都是 type 的左值表达式,因此无论如何重载,都将调用相同的函数b2b3Bgg

在 C++11 中,区分变量的声明和使用该变量产生的表达式比以往任何时候都更加重要。并且表达式永远没有引用类型。相反,它们的值类别恰好是以下之一:左值、xvalue、prvalue。

该声明:

A(std::move(c))

没关系,因为std::move返回一个右值引用。返回右值引用的函数调用产生的表达式具有值类别:xvalue。与纯右值一起,xvalues 被认为是右值。和类型的右值表达式C

std::move(c)

将绑定到 rvalue 引用参数:A(A&& a)

我发现下图(最初由 Bjarne Stroustrup 发明)非常有用:

       expression
          /  \
    glvalue  rvalue
     /  \    /  \
lvalue  xvalue  prvalue
于 2012-04-19T23:24:22.080 回答
13

因为命名变量不是右值,即使声明了 &&。如果它有名称,则它不是临时的,因此您需要使用std::move.

于 2012-04-19T22:53:14.427 回答