Search code examples
c++c++11move-semanticsrvalue-reference

Will a value be moved in this case


Consider the following snippet:

class Bar {...};
class Foo {
  public:
    Foo(const Bar& bar): bar_(bar) {}
  private:
    Bar bar_;
};

int main() {
    Foo foo({...});  // Passing an rvalue Bar object here.
}

Question: will the object that I have generated be copied in the Foo's constructor, or will it be moved? More precisely, which of the following is true?

  • It will be copied always.
  • It will be moved always (I'm pretty sure it is not the case).
  • This is unspecified behavior.

Solution

  • It will be copied always.

    The rvalue you create binds to a const lvalue reference. But even if it was bound to an rvalue reference, you construct bar_(bar) from an lvalue (bar). The value category of the object that is bound to the reference is moot, what matters is the value category of the reference itself. And like already mentioned, the expression bar is an lvalue.

    As a general and somewhat coarse rule of thumb, every expression that is a name of something is an lvalue.

    If you want to support both moves and copies in one constructor, the way to go forward is this:

    Foo(Bar bar): bar_(std::move(bar)) {}
    

    Now bar can be constructed by either a move of a copy, depending on the source, and your member is constructed from an object you can safely move out of. This of course always incurs an extra move. If you want to avoid it, then two c'tors are required:

    Foo(const Bar& bar): bar_(bar) {}
    Foo(Bar&& bar): bar_(std::move(bar)) {}