11

Consider the following:

struct X
{
    Y y_;

    X(const Y & y) :y_(y) {}    
    X(Y && y) :y_(std::move(y)) {}
};

Is it necessary to define a constructor like the second one in order to take full advantage of move semantics? Or will it be taken care of automatically in the appropriate situations?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • A move constructor or assignment operator is implicitly generated [in certain circumstances](http://stackoverflow.com/questions/4819936/why-no-default-move-assignment-move-constructor/4820339#4820339). Since you've declared a copy constructor here, you would also have to declare and define an appropriate move constructor yourself. However, you could remove both of those and have the same behavior in your specific case. – James McNellis Jan 29 '11 at 21:35
  • 1
    @James: Handy information. However, I didn't define a copy constructor here. I defined a constructor from Y to X. – Benjamin Lindley Jan 29 '11 at 21:40
  • Oh. Sorry about that, I only saw letters and figured they were all the same. – James McNellis Jan 29 '11 at 21:41

2 Answers2

7

Yes, but no. Your code should just be this:

struct X
{
    Y y_;

    X(Y y) : // either copy, move, or elide a Y
    y_(std::move(y)) // and move it to the member
    {} 
};

If you ever say in design "I need my own copy of this data"*, then you should just take the argument by value and move it to where it needs to be. It isn't your job to decide how to construct that value, that's up to the available constructors for that value, so let it make that choice, whatever it is, and work with the end result.

*This applies to functions too, of course, for example:

void add_to_map(std::string x, int y) // either copy, move or elide a std::string
{
    // and move it to where it needs to be
    someMap.insert(std::make_pair(std::move(x), y));
}

Note that is applied in C++03 too, somewhat, if a type was default constructible and swappable (which is all moving does anyway):

// C++03
struct X
{
    std::string y_;

    X(std::string y) // either copy or elide a std::string
    {
        swap(y_, y); // and "move" it to the member
    } 
};

Though this didn't seem to be as widely done.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
1

Yes, it is necessary. A const ref can only be a copy, not a move.

Puppy
  • 144,682
  • 38
  • 256
  • 465