Why does destructor disable generation of implicit move methods?
Asked Answered
M

4

22

I was trying to understand what the rule of zero says by reading this blog. IMO, it says if you declare your own destructor then don't forget to make the move constructor and move assignment as default.

Example:

class Widget {
public:
  ~Widget();         // temporary destructor
  ...                // no copy or move functions
};

"The addition of the destructor has the side effect of disabling generation of the move functions, but because Widget is copyable, all the code that used to generate moves will now generate copies. In other words, adding a destructor to the class has caused presumably-efficient moves to be silently replaced with presumably-less-efficient copies".

The above text by Scott Meyers, inside the quotes raise some questions in my mind:

  • Why declaring destructor hides the move semantics?
  • Is declaring/definig destructor only hides the move semantics or copy constructor and copy assignment as well hides the move semantics?
Mastigophoran answered 26/11, 2015 at 7:29 Comment(3)
The rationale behind this rule was that if your code has a custom destructor, it's probably because a resource needs to be freed; and if a resource needs to be freed then the default implementation of move may leave the source object in a bogus state.Kaminsky
It is not clear what "Is declaring/definig destructor only hides the move semantics or copy constructor and copy assignment as well hides the move semantics?" is asking.Duffel
The link mentioned in the question is not functional.Kaneshakang
G
24

"The Rule of Zero" is in fact about something else than what special member functions are generated and when. It is about a certain attitude to class design. It encourages you to answer a question:

Does my class manage resources?

If so, each resource should be moved to its dedicated class, so that your classes only manage resources (and do nothing else) or only accumulate other classes and/or perform same logical tasks (but do not manage resources).

It is a special case of a more general Single Responsibility Principle.

When you apply it, you will immediately see that for resource-managing classes you will have to define manually move constructor, move assignment and destructor (rarely will you need the copy operations). And for the non-resource classes, you do not need to (and in fact you probably shouldn't) declare any of: move ctor/assignment, copy ctor/assignment, destructor.

Hence the "zero" in the name: when you separate classes to resource-managing and others, in the "others" you need to provide zero special member functions (they will be correctly auto-generated.

There are rules in C++ what definition (of a special member function) inhibits what other definitions, but they only distract you from understanding the core of the Rule of Zero.

For more information, see:

  1. https://akrzemi1.wordpress.com/2015/09/08/special-member-functions/
  2. https://akrzemi1.wordpress.com/2015/09/11/declaring-the-move-constructor/
Gambia answered 26/11, 2015 at 7:49 Comment(0)
S
6

Almost always, if you have a destructor (that "does something"), you should follow the "rule of three", which then becomes "rule of five" if you want move semantics.

If your destructor is empty, then it's not needed. So the implication is that a non-empty destructor (because you wouldn't have one if it's not needed!), then you also need to do the same thing in copy and assignment operations, and presumably, move construction and move assignment will need to "do something", and not just transfer the actual content across.

Of course, there may be cases where this is not true, but the compiler takes the approach of "let's only apply the automatically generated move functions if the destructor is empty", because that is the "safe" approach.

Shipley answered 26/11, 2015 at 7:40 Comment(2)
Hi Pete many thanks for your response, from what you are saying, the compiler will hide the implicit move if we explicitly define the Dtor(safe approach)...for what safety purpose compiler does the hiding... i read the copy operation cost high when compare to move... is the hiding happen though we define the move explicitly(i don't think so).Mastigophoran
So, the general idea is that "it is safe to move something if it doesn't have a destructor". A destructor implies that destroying the object requires some special action (deleting something, counting a reference count down, etc). In move semantics, this is done by "swapping" the object from one side to another, rather than making a a copy. Of course, how this plays with "whatever you need to do in the destructor" is impossible for the compiler to actually know (unless it understands what the effect of the destructor is, and it doens't take much before that is impossible to know!)Shipley
H
3

is declaring/defining Dtor only hide the move semantics or copy ctor/copy assignment as well hide the move semantics?

If no user-defined move constructors are provided for a class all of the following is true:

  • there are no user-declared copy constructors
  • there are no user-declared copy assignment operators
  • there are no user-declared move assignment operators
  • there are no user-declared destructors

then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).

Thus, yes declaring a copy constructor or an assignment operator hides implicitly declared move constructor as well.

Hance answered 26/11, 2015 at 7:45 Comment(3)
I think OP is asking for the rationale behind these rules.Lucania
Many thanks 101010, Under what basis the hiding happens is there any rule like R0/R3/R5 or it is up to the compiler.Mastigophoran
No these are standard rules all compilers follow them.Hance
D
1

First I would say Mats Petersson's answer is better than the accepted one as it mentions the rationale.

Second, as a supplement, I want to elaborate a bit more.

The behavior of implicitly-declared (or defaulted) move ctor

From c++draft:

The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.

The condition when compiler implicitly declares a move ctor

From cppreference:

If no user-defined move constructors are provided for a class all of the following is true:

  • there are no user-declared copy constructors
  • there are no user-declared copy assignment operators
  • there are no user-declared move assignment operators
  • there are no user-declared destructors

then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).

Why dtor(and many others) prevents implicitly-declared move ctor?

If we look at the above conditions, not only user-declared destructor prevents implicitly-declared move ctor, user-declared copy constructor, user-declared copy assignment operator and user-declared move assignment operator all has the same prevention effect.

The rationale, as Mats Petersson has pointed out, is that:

If the compiler thinks you might need to do something other than memberwise move in move operation, then it is not safe to assume you don't need it.

  • When there are user-declared destructors, which means there's some clean up work to do, then you probably want to do it with the moved-from object.

  • When there are user-declared move assignment operators, since it's also "moving" resources, you probably want to do the same in move ctor.

  • When there are user-declared copy constructors or copy assignment operators, this is the most interesting case. We know that move semantics allows us to keep value semantics while gaining performance optimization, and that move will "fall back" to copy when move ctor is not provided. In some way, move can been seen as the "optimized copy". Therefore, if the copy operation requires us to do something, it is likely that similar work is also needed in move operation.

Since in the above conditions, something other than memberwise move might needs to be done, the compiler will not assume you don't need it, and thus won't implicitly declare a move ctor.

Duffel answered 23/5, 2018 at 14:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.