How to take an rvalue reference parameter and pass it on elsewhere?

99 Views Asked by At

I am a reasonably competent C++ user (not a complete noob). I have a class that acts as a resource handle. It makes sense for the class to have a move constructor and for the copy construtor to be deleted:

struct Foo {

  Foo (int i) : // For sake of example.
    x(i)
  {}

  Foo (Foo && f) :
    x(f.x)
  {
    f.x = 0; // 0 is special and means "moved from".
  }

  Foo (const Foo & f) = delete;

private:
  int x;
};

I've been doing this in a cargo-cult fashion for ages now and it works fine. Now I'm trying to step up a gear with my C++11 usage.

I have another class that keeps a vector of Foo:

struct Bar { // (Constructor added below)
  std::vector<Foo> foos;
};

I'd like to write a constructor for Bar where the caller passes in a vector<Foo>. I'd like the entire vector that the caller provides to be moved into Bar.foos. I want to make that explicit to the caller by making the constructor parameter a vector<Foo>&& rather than a plain vector<Foo>&. That way, the caller must std::move the vector in to the constructor.

int main (int argc, char ** argv)
{     
    Foo f1 (1);
    Foo f2 (2);
    std::vector<Foo> v;
    v.push_back (std::move(f1));
    v.push_back (std::move(f2));

    Bar b (std::move(v)); // I want the user to HAVE TO std::move v.
    return 0;
}

I naively tried writing Bar constructor like this:

struct Bar {

  Bar (vector<Foo> && vf) :
    foos (vf) // pass the rvalue reference into foos' constructor, right?
  {}

  std::vector<Foo> foos;
};

My computer has g++ 4.9.2 and clang++ 3.5.0 and they both give me a small explosion of errors. They're both trying to construct Bar.foos using the vector copy constructor, which then fails because I've deleted the copy constructor of Foo.

How can I give 'vf', the rvalue reference of vector<Foo> straight to the constructor of 'foos'?

2

There are 2 best solutions below

1
On BEST ANSWER

A named argument is not an rvalue, so you have to convert vf to an rvalue reference by calling std::move:

Bar(std::vector<Foo>&& vf) : foos(std::move(vf) {}

Also, it's not quite correct to say that the user must call std::move for an rvalue parameter; there's no need for std::move when the argument is the return value of a function, which is the classic notion of an rvalue.

0
On

Typically, when you have a parameter of move-only type, you take it by value:

struct Bar
{
    std::vector<Foo> foos;

    Bar(vector<Foo> vf) :
        foos(std::move(vf)a) {}
};

This forces the caller to write

Bar x(std::move(y));

which makes clear that ownership of y is relinquished. In addition, the caller can just pass the return value of a function without further ado:

Bar x(get_me_some_Foos());