局部变量的右值引用和移动

rvalue reference and move of a local variable

在此代码段中,我分配了一个本地对象 B,我将其传递给另一个对象 C 的构造函数,后者将其作为右值引用。然后我将后者放入容器中。

当我检索 C 并将其成员 static_cast 返回给 B 时,值不正确(并且 valgrind 识别出 18 个错误!)

#include <iostream>
#include <memory>
#include <vector>

class A {
public:
  virtual ~A() {};
};

class B: public A {
public:
  B(int foo) : _foo(foo) {}
  int _foo;
};

class C {
public:
  C(A&& a): _a(std::move(a)) {}
  A&& _a;
};

std::vector<C> v;

void bar() {
  v.emplace_back(B(12));
}

int main() {
  bar();
  C&& c = std::move(v.front());
  std::cout << static_cast<B&&>(c._a)._foo << std::endl;
  return 0;
}

我的理解是,由于 C 采用右值引用,因此不应该有任何对象切片。我应该仍然能够检索完整的 B 对象。此外,我对 std::move 的理解(很可能是有缺陷的)是我可以 'move' 脱离其上下文的局部变量,并且通过采用右值引用,C 获得 B 的所有权。

输出是 39931504 而不是 12

谁能给我解释一下这是怎么回事?

I should still be able to retrieve a full B object.

如果它不是一个在控件到达 bar 的右大括号时不存在的临时文件,您就会这样做。您在 C 中持有一个引用,对临时对象的引用很快就会变得悬空。

一个快速修复方法是简单地将 C 的成员声明更改为 std::unique_ptr<A> a; 并正确初始化它,例如

template<class AChild> C(AChild child)
  : a(std::make_unique<AChild>(std::move(child))) {};

(按口味添加 SFINAE)。

问题是 class C 中对 A 的引用(即数据成员 _a)比它所指的对象存在时间更长。这是因为 temporary B(12) 在你的 bar() 函数中

void bar() {
   v.emplace_back(B(12));
}

bar() 返回后不存在。因此,class C 包含的对 A 的引用变成了 悬空引用 .


您可以使用 new 运算符创建 B 对象而不是临时对象:

void bar() {
   B* ptr = new B(12);
   v.emplace_back(std::move(*ptr));
}

bar() 返回时,此 B 对象不会停止存在,因此 C 中对 A 的引用仍然有效。

请注意,使用此方法您需要手动销毁对象:

C&& c = std::move(v.front());
// ...
delete &c._a; // destroy object