43

I want to prevent the user of my class from using it as an automatic variable, so I write code like this:

class A {
private:
  ~A() = default;
};

int main() {
  A a;
}

I expect that the code won't be compiled, but g++ compiles it without error.

However, when I change the code to:

class A {
private:
  ~A(){}
};

int main() {
  A a;
}

Now, g++ gives the error that ~A() is private, as is my expectation.

What's the difference between a "= default" destructor and an empty destructor?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
delphifirst
  • 1,781
  • 1
  • 14
  • 23
  • 4
    What version of gcc? –  Jan 04 '15 at 04:03
  • 1
    Reading e.g. [this destructor reference](http://en.cppreference.com/w/cpp/language/destructor), the difference is that a user-provided destructor (even if it's empty) is *non-trivial*, and from the reference: "Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage." – Some programmer dude Jan 04 '15 at 04:03
  • 8
    In your case there is no difference, both [gcc4.9 and clang3.5 refuse to compile](http://coliru.stacked-crooked.com/a/41976bc03bc12c79) your first example. It must be a bug in your version of gcc. – Praetorian Jan 04 '15 at 04:04
  • The version of my gcc is 4.8.2 – delphifirst Jan 04 '15 at 04:07
  • 4
    I think the relevant bug report is [54812](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54812) –  Jan 04 '15 at 04:08
  • @JoachimPileborg When it says it doesn't require a delete-expression, does it mean when the object is dynamically allocated? And by "deallocating their storage", does it mean call the destructor? – David G Jan 04 '15 at 04:12
  • @0x499602D2 Calling of the destructor is part of the deallocation process, but it not what's meant with "deallocating their storage". Think about the old C `malloc` and `free` function, allocating storage is done through `malloc` and freeing is done through `free`, but they only allocate/free the actual storage. You could actually implement the `new` and `delete` operators with `malloc` and `free`, and for `delete` you call the destructor (if it's non-trivial) and *then* free the storage with `free`. – Some programmer dude Jan 04 '15 at 04:16
  • `I want to prevent the user of my class from using it as an automatic variable` Hmmm... private destructor will prevent your class from being used in almost any form. Maybe you meant protected? – mip Jan 04 '15 at 04:30
  • @doc: A private destructor is fine for many use case scenarios, just making a common deleter function a `friend`. That deleter function can be e.g. `std::default_delete`. I think it's more or less a defect that it isn't guaranteed used by default by `std::shared_ptr`. – Cheers and hth. - Alf Jan 04 '15 at 06:06

1 Answers1

32

Your first example should not compile. This represents a bug in the compiler that it does compile. This bug is fixed in gcc 4.9 and later.

The destructor defined with = default is trivial in this case. This can be detected with std::is_trivially_destructible<A>::value.

Update

C++11 (and C++14) state that if one has a user-declared destructor (and if you don't have either user-declared move special member), then the implicit generation of the copy constructor and copy assignment operator still happen, but that behavior is deprecated. Meaning if you rely on it, your compiler might give you a deprecation warning (or might not).

Both:

~A() = default;

and:

~A() {};

are user-declared, and so they have no difference with respect to this point. If you use either of these forms (and don't declare move members), you should explicitly default, explicitly delete, or explicitly provide your copy members in order to avoid relying on deprecated behavior.

If you do declare move members (with or without declaring a destructor), then the copy members are implicitly deleted.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 2
    @delphifirst: Compiler bugs are very common these days. When I was a student, in the early 1980s, we learned that for sure any ungood behavior was a bug in our code, not in the compiler. Today I regularly make compilers crash (ICE, Internal Compiler Error), and then there's no question about whose fault it is... – Cheers and hth. - Alf Jan 04 '15 at 04:56
  • @HowardHinnant: I miss some discussion of defaulted versus not declared. I think that would relate to the standard's use of the phrase "user declared", e.g. in C++11 §12.8/18. Please? – Cheers and hth. - Alf Jan 04 '15 at 05:01
  • @Cheersandhth.-Alf: Glad to help, but I don't understand your question. If you're asking if an `=defaulted` special member is user-declared, the answer is yes, but not user-provided. – Howard Hinnant Jan 04 '15 at 15:40
  • 1
    @HowardHinnant: It goes to the OP's question literally "What's the difference between a "= default" destructor and an empty destructor?". A difference about future meaning of the code, as in the paragraph I referred to, is IMHO very relevant. It's certainly a difference. – Cheers and hth. - Alf Jan 04 '15 at 17:59
  • @Cheersandhth.-Alf: You are referring to the compiler implicitly providing the copy members and how that is deprecated in certain situations? – Howard Hinnant Jan 04 '15 at 18:44
  • Can you elaborate or link to "behavior is deprecated?" – Christopher Pisz Jul 09 '20 at 03:56
  • For the copy constructor: http://eel.is/c++draft/class.copy.ctor#6 For the copy assignment operator: http://eel.is/c++draft/class.copy.assign#2 – Howard Hinnant Jul 09 '20 at 13:53