56
struct A
{
    ~A() = delete;
};

int main()
{
    new A{};
}

This fails to compile with error message:

error: use of deleted function 'A::~A()' new A{};

As I understand I'm not destroying the object so why is it trying to call the destructor?

Compiled with GCC 8.1.0

g++ -std=c++17 -O2
PoweredByRice
  • 2,479
  • 1
  • 20
  • 26
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/178570/discussion-on-question-by-poweredbyrice-why-is-the-destructor-called-for-an-obje). – Bhargav Rao Aug 22 '18 at 22:36
  • Note that [it's possible to reproduce the bug without causing memory leak](https://tio.run/##Sy4o0E3OScxL//8fSBUXKzhWcxUUZZYllqRacXHWOWpoVtdac3GmFWWm5qUolOVnpiikpOaklqTGO2o4amlacwFl0UUVKjSrIVyFCutarsy8EoXcxMw8oFFcnCBZ27zUcqA9IHPhuipARv3/DwA). The example in the question is not really practical, as it doesn't `delete` a `new`'ed object. – user202729 Aug 23 '18 at 00:54

3 Answers3

37

This is gcc bug 57082.


Let's go from the bottom up.

[dcl.fct.def.delete]/2:

A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

Clearly, we're not referring to ~A() explicitly. Are we referring to it implicitly? [class.dtor]/12:

A destructor is invoked implicitly

  • for a constructed object with static storage duration ([basic.stc.static]) at program termination ([basic.start.term]),
  • for a constructed object with thread storage duration ([basic.stc.thread]) at thread exit,
  • for a constructed object with automatic storage duration ([basic.stc.auto]) when the block in which an object is created exits ([stmt.dcl]),
  • for a constructed temporary object when its lifetime ends ([conv.rval], [class.temporary]).

Or in [expr.new]/20:

If the new-expression creates an array of objects of class type, the destructor is potentially invoked.

Do we have any of those things? No, there is no object with automatic, static, or thread storage duration here, nor is there a constructed temporary object, nor is our new-expression creating an array. There is only one object here at all, the one A with dynamic storage duration that we're aggregate-initializing.

Since we're neither explicitly nor implicitly referring to ~A(), we can't be tripping over that rule. Hence, gcc bug. Note also that gcc accepts new A; and new A();, which have the same meaning as far as this rule is concerned.

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    You missed a case of destructor invoked for elements of the array when one of the elements throws in constructor (discussed extensively in other answer) – SergeyA Aug 22 '18 at 21:24
  • @SergeyA According to the standard even if the constructor is noexcept the destructor is potentially invoked. – Oliv Aug 22 '18 at 21:25
  • @Oliv, sure, you nailed it in your answer. I am just showing a (simplified) case to Barry. – SergeyA Aug 22 '18 at 21:26
  • @Barry, fair enough, you were referring to a specific case. Ironically, looks like it might be relevant :) – SergeyA Aug 22 '18 at 21:28
  • @Barry On the other hand neither a static storage duration nor a thread storage duration,... is created here!! – Oliv Aug 22 '18 at 21:28
  • It is becoming funny, so neither a static storage duration, ... nor an array are created here! The problem seems to be that gcc has some how inverted the rule I quote in [expr.new]. gcc accepts new A[10] while it should not and reject new A{} while it should accept it! Our answer are complementary. – Oliv Aug 22 '18 at 21:32
  • @user2079303 Okay yeah, I don't know what I was thinking earlier. – Barry Aug 22 '18 at 21:35
4

Probably a gcc bug here.

The standard specifies that the destructor is potentially invoked when the new expression create an array [expr.new]:

If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function, and the constructor. If the new-expression creates an array of objects of class type, the destructor is potentially invoked.

emphasis mine

gcc applies also this rule when creating a non array, which is implicitly not a standard rule. Thanks to below comments it seems gcc do the exact opposite: when creating a non array, it considers the destructor to be potentialy invoked and when creating an array it just don't check the destructor.

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • Technically, the quoted rule doesn't say that the example program isn't ill-formed. It does seem like a good hint to why the bug might occur. – eerorika Aug 22 '18 at 21:09
  • @RicharCritten And in this case clang reject the code: https://godbolt.org/z/xMMrKh. Maybe just an extra ! character inside gcc code!! – Oliv Aug 22 '18 at 21:15
  • Yup looking like a bug – Richard Critten Aug 22 '18 at 21:16
  • The fact that `new A[10]` compiles is puzzling, because what if `A()`throws? Generated code in this case pretends no destructor needs to be called, which is incorrect. Strongly looks like a bug in gcc. – SergeyA Aug 22 '18 at 21:16
  • Live with A() throwing: https://godbolt.org/z/7f7CFG How about because `A` is POD no destructor is needed? Making `A` non-POD gives the expected error: https://godbolt.org/z/7P-4Ph – Richard Critten Aug 22 '18 at 21:18
  • @RichardCritten I have a very similar example, and compiler is not allowed to generate such code. Destructor is guaranteed to be called for objects already constructed if one of constructor throws. – SergeyA Aug 22 '18 at 21:22
  • If the compiler can analyse an object to be POD (destructor would be a NOP) and the standard says _"...potentially invoked..."_; is the compiler allowed to not require the destructor? – Richard Critten Aug 22 '18 at 21:27
  • Making `A` non-POD and not allocating an array compiles: https://godbolt.org/z/BhyfFY – Richard Critten Aug 22 '18 at 21:41
  • @RichardCritten I have just found out this: [basic.def.odr] *A destructor for a class is odr-used if it is potentially invoked* (thank you pdf search!) – Oliv Aug 22 '18 at 21:42
  • @Oliv thanks for that and also for not telling that I should be asking in a new question. – Richard Critten Aug 22 '18 at 21:42
  • @RichardCritten Maybe with the erroneous array: https://godbolt.org/z/E8Qpqk. Looking inside the bug reported by Barry it seems to have been linked to this other [bug](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78469) which was declared fixed. I wonder if in this case we should report a new bug? – Oliv Aug 22 '18 at 21:55
  • @RichardCritten I posted this [bug](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87066). I hope it will help. – Oliv Aug 22 '18 at 22:18
  • Thanks for putting in the effort to write the report – Richard Critten Aug 22 '18 at 22:20
2

As far as I can tell, no objects are destroyed in the example, and it happens to compile if the expression is changed to new A;

I think that the example code not compiling is is a bug in GCC. Clang compiles it just fine.


Answer for the newly added language-lawyer tag.

The crucial standard rule is this in [class.dtor]:

A destructor is invoked implicitly

... cases that don't apply involving other storage durations than dynamic ...

... A destructor is also invoked implicitly through use of a delete-expression (5.3.5) for a constructed object allocated by a new-expression (5.3.4); the context of the invocation is the delete-expression. [ Note: An array of class type contains several subobjects for each of which the destructor is invoked. — end note ] A destructor can also be invoked explicitly. A destructor is potentially invoked if it is invoked or as specified in 5.3.4, 12.6.2, and 15.1.

5.3.4 is [expr.new] which only specifies

... If the new-expression creates an array of objects of class type, the destructor is potentially invoked (12.4).

which doesn't apply.

12.6.2 is [class.base.init] which only specifies

In a non-delegating constructor, the destructor for each potentially constructed subobject of class type is potentially invoked (12.4).

Which doesn't apply

15.1 is [except.throw] which specifies how an exception object is destroyed, which doesn't apply

Conclusion: None of sections 5.3.4, 12.6.2, and 15.1. contain a rule that applies to this case, and the destructor isn't invoked, nor is there a delete-expression. Therefore the destructor isn't potentially invoked, so it is well formed for the destructor to be deleted.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 3
    Not enough answer in `language-lawyer`, sorry. You need to add way more Standarteese to make it a proper answer in this tag. – SergeyA Aug 22 '18 at 20:54
  • @SergeyA I've looked up the standard rule from [class.dtor] which I believe is most relevant. – eerorika Aug 22 '18 at 21:49