6

I cannot understand the rationale behind the automatic addition of default ctors. In particular I find very awkward that every single time I just need to add an empty virtual destructor and nothing more, I loose the move stuffs, but adding them I loose the copy and default things, so I end up adding all this chunk of code:

virtual ~SomeClass(){}           // you are the guilty!
//virtual ~SomeClass() = default // would be the same

SomeClass(SomeClass&&) = default;                 // no more auto-added
SomeClass& operator=(SomeClass&&) = default;      // no more auto-added
SomeClass(const SomeClass&) = default;            // but with the moves defined,
SomeClass& operator=(const SomeClass&) = default; // I'm now missing the copy
SomeClass(){}                                     // and the default as well

I'm sure there is a reason for making my classes ugly and letting me desire an evil macro, I just would like to know it to feel more comfortable.

DarioP
  • 5,377
  • 1
  • 33
  • 52
  • That's how it's specified to behave. See e.g. [this reference](http://en.cppreference.com/w/cpp/language/move_constructor). – Some programmer dude Jul 24 '14 at 15:01
  • 1
    Instead of adding a defaulted or empty virtual dtor, you can simply derive from a class that has a a virtual dtor. `struct make_polymorphic { virtual ~make_polymorphic() = default; };` [Live example](http://coliru.stacked-crooked.com/a/133459bcc0677b22) – dyp Jul 24 '14 at 15:15
  • Funny. When I use polymorphic classes (i.e. with a virtual destructor) I tend to prefer not having copies or moves, because I end up using the things by reference anyway. – R. Martinho Fernandes Jul 24 '14 at 16:57
  • @R.MartinhoFernandes I thought about that: I can avoid moves, but copies are essential, indeed I'm making extensive use of the clone pattern. Since the cctor is still auto-defined I guess I can avoid any explicit declaration although Mike Seymour is saying that this is deprecated in C++11... – DarioP Jul 25 '14 at 07:33

2 Answers2

1

Take a look at this. It explains something called the rule of five, which is essentially what standard requires.

Generally, for most cases, compiler creates defaults for copy constructor, copy assignment, move assignment, and destructor. But, if a programmer defines any of these, then the compiler assumes the user has encapsulated something in this class that requires his/her special, let's say. destructor. Now that the programmer knows that he/she is going to need a destructor, the compiler will know that the programmer know what's going on and just not create the defaults for the rest (because, based on the assumption that the compiler makes, the default ones are going to be wrong, and can even result in undesired behavior).

Community
  • 1
  • 1
triple_r
  • 1,037
  • 1
  • 8
  • 21
  • If I just need to flag my destructor as virtual and nothing more, the compiler should not have hard times in guessing that all the other defaults are still valid... I cannot find an example of something that would be broken by this. – DarioP Jul 25 '14 at 07:44
  • @DarioP probably I'm too late and you have gone on to better things, but just wanted to share this article that I came across today: [To Move or Not to Move](http://www.stroustrup.com/move.pdf) by B. Stroustrup. He talks about many scenarios that introducing a default move/copy operator can break things. Just a good read I suppose. – triple_r Aug 04 '14 at 18:54
0

The problem is that your class is trying to do two separate things: providing a polymorphic interface (hence the need for the virtual destructor) and managing concrete data members (hence the need for the copy/move operations). It's generally a good idea to give each class a single responsibility.

I'd move the virtual destructor, and any virtual function declarations, to an empty, abstract base class. Then any concrete class(es) deriving from that will be free to autogenerate all the needful things.

Example:

#include <iostream>

struct Movable {
    Movable() {}
    Movable(Movable&&m) {std::cout << "Moving\n";}
};

struct SomeInterface {
    virtual ~SomeInterface() {}

    // no data members, so no need for any other special member functions
};

struct SomeClass : SomeInterface {
    Movable stuff;

    // no user-declared special functions, so all are auto-generated
};

int main() {
    SomeClass c;
    SomeClass c2(std::move(c));  // uses the auto-generated move constructor
}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Thanks for providing a workaround other than an evil macro. Still I'm quite not getting the rationale... why the default copy ctors are still defined when I add my virtual destructor? – DarioP Jul 24 '14 at 15:30
  • @DarioP: The copy functions are generated even if there's a user-declared destructor because that's how C++ has always worked, and changing that would break a lot of existing code. (C++11 deprecates that behaviour, but doesn't forbid it). Move semantics are new, so aren't constrained by compatibility with older dialects. – Mike Seymour Jul 24 '14 at 15:40
  • Is that really *deprecated* in C++11? – DarioP Jul 25 '14 at 07:31
  • @DarioP: Yes, see 12.8/7. – Mike Seymour Jul 25 '14 at 10:08