Is declaring explicitly defaulted move constructor in every class that doesn't provide user-defined one a good practice?
Asked Answered
P

1

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.

Pindling answered 27/2, 2018 at 7:25 Comment(4)
My opinion: I think it is always reasonable and good to be explicit.Deckhand
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.Datum
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.Schelling
"Rule of five" and "rule of zero" are both good just please get noexcept correctly if you go with rule of five.Olivia
U
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.

Ultraconservative answered 27/2, 2018 at 7:42 Comment(7)
Your class is wrong even without any defaulted move or copy constructors. It causes crash when copying: coliru.stacked-crooked.com/a/f2f0031b99de7c8b. You have to either add user-declared copy constructor or declare copy constructor as deleted.Pindling
It would probably make sense to omit generation of default copy constructor if a class has user-declared destructor.Pindling
@Pindling You're right, this is valid only for move constructor, not copy constructor, which in generated as well.Ultraconservative
But anyway your example indirectly shows the harm of default copy/move constructors: coliru.stacked-crooked.com/a/55829815553804d0Pindling
@Pindling I updated the answer about move constructor only.Ultraconservative
@Pindling You're welcome. This reasoning may be even stronger for non-copyable classes with deleted copy constructor, since then there is higher chance that one will forget to provide user-defined move constructor in the updated class.Ultraconservative
Since it does not have const or reference members it still generates copy-assignment that can crash a program as finely.Olivia

© 2022 - 2024 — McMap. All rights reserved.