Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rvalue reference and move of a local variable

In this snippet, I allocate a local object B that I pass to the constructor of another object C which takes it as a rvalue reference. I then put the latter into a container.

When I retrieve C and static_cast its member back to B, the value is incorrect (and valgrind identifies 18 errors!)

#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;
}

My understanding is that, as C takes a rvalue reference, there should not be any object slicing. I should still be able to retrieve a full B object. Additionally, my understanding (most probably flawed) of std::move is that I can 'move' a local variable out of its context and that by taking a rvalue reference, C takes ownership of B.

The output is 39931504 instead of 12.

Anyone could explain to me what is going on?

like image 996
Luke Skywalker Avatar asked Oct 15 '22 10:10

Luke Skywalker


1 Answers

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

You would be if it wasn't a temporary that does not exist by the moment control reaches bar's closing brace. You hold a reference in your C and a reference to a temporary object can quickly become dangling.

A quick fix is to simply change C's member declaration into std::unique_ptr<A> a; and properly initialise it, say, as

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

(add SFINAE by taste).

like image 104
bipll Avatar answered Nov 15 '22 09:11

bipll