web-dev-qa-db-fra.com

Pourquoi le destructeur est-il appelé pour un objet qui n'est pas supprimé?

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

int main()
{
    new A{};
}

Cette compilation échoue avec le message d'erreur suivant:

erreur: utilisation de la fonction supprimée 'A :: ~ A ()' new A {};

Si je comprends bien, je ne détruis pas l’objet, alors pourquoi tente-t-il d’appeler le destructeur?

Compilé avec GCC 8.1.0

g++ -std=c++17 -O2
59
PoweredByRice

C'est bug gcc 57082 .


Allons de bas en haut.

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

Un programme qui fait référence à une fonction supprimée implicitement ou explicitement, autre que de la déclarer, est mal formé.

Clairement, nous ne faisons pas référence à ~A() explicitement. Est-ce qu'on en parle implicitement? [class.dtor]/12 :

Un destructeur est invoqué implicitement

  • pour un objet construit avec une durée de stockage statique ([basic.stc.static]) à la fin du programme ([basic.start.term]),
  • pour un objet construit avec une durée de stockage de thread ([basic.stc.thread]) à la sortie du thread,
  • pour un objet construit avec une durée de stockage automatique ([basic.stc.auto]) à la sortie du bloc dans lequel un objet est créé ([stmt.dcl]),
  • pour un objet temporaire construit à la fin de sa durée de vie ([conv.rval], [class.temporary]).

Ou dans [expr.new]/2 :

Si la nouvelle expression crée un tableau d'objets de type classe, le destructeur est potentiellement appelé.

Avons-nous une de ces choses? Non, il n'y a aucun objet avec une durée de stockage automatique, statique ou de thread ici, pas plus qu'il n'y a d'objet temporaire construit, ni notre nouvelle-expression créant un tableau. Il n'y a qu'un seul objet ici, celui A avec une durée de stockage dynamique que nous sommes en train d'initialiser des agrégats.

Puisque nous ne faisons ni explicitement ni implicitement référence à ~A(), nous ne pouvons pas nous écarter de cette règle. Par conséquent, bug gcc. Notez également que gcc accepte new A; Et new A();, qui ont la même signification pour cette règle.

38
Barry

Probablement un bug de gcc ici.

La norme spécifie que le destructeur est potentiellement appelé lorsque la nouvelle expression crée un tableau [expr.new] :

Si new-expression crée un objet ou un tableau d'objets de type classe, les contrôles d'accès et d'ambiguïté sont effectués pour la fonction d'allocation, la fonction de désaffectation et le constructeur. Si la nouvelle expression crée un tableau d'objets de type classe, le destructeur est potentiellement appelé.

l'accent mien

gcc applique également cette règle lors de la création d'un non-tableau, ce qui implicitement n'est pas une règle standard. Merci aux commentaires ci-dessous, il semble que gcc fasse exactement le contraire: lors de la création d'un non tableau, il considère que le destructeur est potentiellement appelé et lors de la création d'un tableau, il ne vérifie pas le destructeur.

4
Oliv

Autant que je sache, aucun objet n'est détruit dans l'exemple, et il arrive de compiler si l'expression est changée en new A;

Je pense que l'exemple de code non compilé est un bogue dans GCC. Clang le compile très bien .


Répondez à la balise language-avocats récemment ajoutée.

La règle standard cruciale est la suivante dans [class.dtor]:

Un destructeur est invoqué implicitement

... les cas non applicables impliquant des durées de stockage autres que dynamique ...

... Un destructeur est également appelé implicitement par le biais d'une expression de suppression (5.3.5) pour un objet construit alloué par une nouvelle expression (5.3.4); le contexte de l'invocation est l'expression-suppression. [Remarque: un tableau de type classe contient plusieurs sous-objets pour chacun desquels le destructeur est appelé. - end note] Un destructeur peut également être appelé explicitement. Un destructeur est potentiellement appelé s'il est appelé ou tel que spécifié aux 5.3.4, 12.6.2 et 15.1.

5.3.4 est [expr.new] qui spécifie seulement

... Si la nouvelle expression crée un tableau d'objets de type classe, le destructeur est potentiellement appelé (12.4).

qui ne s'applique pas.

12.6.2 est [class.base.init] qui spécifie seulement

Dans un constructeur non délégué, le destructeur de chaque sous-objet potentiellement construit du type classe est potentiellement appelé (12.4).

Ce qui ne s'applique pas

15.1 est [except.throw] qui spécifie comment un objet d'exception est détruit, ce qui ne s'applique pas

Conclusion: Aucune des sections 5.3.4, 12.6.2 et 15.1. contient une règle qui s'applique à ce cas, et le destructeur n'est pas appelé, pas plus qu'il n'y a une expression de suppression. Par conséquent, le destructeur n'est pas potentiellement appelé, il est donc bien formé pour le supprimer.

2
eerorika