2

There are quite complex (for me) rules that define when implicitly defaulted move constructor is generated and when it is not generated. What I'm afraid of is that the default move constructor won't be generated. Also I'm afraid that I (or someone else) modify the class in the future and implicit move constructor will disappear.

There is an "advice" that says "you can always explicitly invoke the default generation for functions that can be automatically generated with = default (that's what the syntax is for)". And now I'm asking: is it a good idea? Are there any reasons not to do this? I guess if there were no problems with that then we would not need defaulted move constructors, move constructors would just always be generated. But since the Standard defines such strict rules, there probably are reasons for doing that.

anton_rh
  • 8,226
  • 7
  • 45
  • 73
  • 3
    My opinion: I think it is always reasonable and good to be explicit. – MABVT Feb 27 '18 at 07:28
  • 3
    Just because you can doesn't mean you should. As with many other things, it depends on situation and your use-cases. There is no single "yes do this" or "no don't do this" answer to this question. – Some programmer dude Feb 27 '18 at 07:30
  • 1
    In my opinion - and naturally, it's the correct answer: learn the rules then code minimally. Coding to the lowest common denominator is always a smell to me. – Bathsheba Feb 27 '18 at 07:34
  • "Rule of five" and "rule of zero" are both good just please get noexcept correctly if you go with rule of five. – Öö Tiib Feb 27 '18 at 07:41

1 Answers1

1

Consider the following scenario. You have a class that is fine with auto-generated move constructor and destructor:

class X {
      int i_;
   public:
      X(int i) : i_(i) { }
};

Then, in the future, you add a member variable, that requires user-defined copy constructor and destructor:

class X {
      int i_;
      char* buf_;
      size_t n_;
   public:
      X(int i, size_t n) : i_(i), n_(n), buf_(new char[n]) { }
      X(const X& o) : i_(o.i_), n_(o.n_), buf_(new char[o.n_]) { memcpy(buf_, o.buf_, n); }
      ~X() { delete[] buf_; }
};

If move constructor would be defaulted here, the new class version would be wrong.

Without defaulted move constructor, modified class is fine according to rules-of-five which says, that if one of the 5 special function is required to be user-defined, the others are likely required to be user-defined as well. You are now forced to define move constructors manually, if you need it.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • Your class is wrong even without any `default`ed move or copy constructors. It causes crash when copying: http://coliru.stacked-crooked.com/a/f2f0031b99de7c8b. You have to either add user-declared copy constructor or declare copy constructor as `delete`d. – anton_rh Feb 27 '18 at 07:53
  • It would probably make sense to omit generation of default copy constructor if a class has user-declared destructor. – anton_rh Feb 27 '18 at 07:54
  • @anton_rh You're right, this is valid only for move constructor, not copy constructor, which in generated as well. – Daniel Langr Feb 27 '18 at 08:00
  • But anyway your example indirectly shows the harm of default copy/move constructors: http://coliru.stacked-crooked.com/a/55829815553804d0 – anton_rh Feb 27 '18 at 08:00
  • @anton_rh I updated the answer about move constructor only. – Daniel Langr Feb 27 '18 at 08:04
  • 1
    @anton_rh You're welcome. This reasoning may be even stronger for _non-copyable_ classes with `delete`d copy constructor, since then there is higher chance that one will forget to provide user-defined move constructor in the updated class. – Daniel Langr Feb 27 '18 at 08:10
  • Since it does not have const or reference members it still generates copy-assignment that can crash a program as finely. – Öö Tiib Feb 27 '18 at 19:12