web-dev-qa-db-fra.com

Pourquoi un destructeur ne peut-il pas être marqué constexpr?

En C++, vous pouvez déclarer beaucoup de choses comme constexpr : variables, fonctions (y compris les fonctions membres et opérateurs), constructeurs, et depuis C++ 1z, aussi if instructions et expressions lambda . Cependant, déclarer un destructeurconstexpr entraîne une erreur:

struct X {
    constexpr ~X() = default; // error: a destructor cannot be 'constexpr'
};

Mes questions:

  1. Pourquoi un destructeur ne peut-il pas être marqué constexpr?
  2. Si je ne fournis pas de destructeur, le destructeur généré implicitement est-il constexpr?
  3. Si je déclare un destructeur par défaut (~X() = default;), est-ce automatiquement constexpr?
34
s3rvac

Selon le type de classe draft basic.types # 1 éventuellement qualifié cv qui possède toutes les propriétés suivantes:

Un type de classe éventuellement qualifié par cv qui possède toutes les propriétés suivantes:

(10.5.1) - il a un destructeur trivial,

(10.5.2) - il s'agit soit d'un type de fermeture, d'un type d'agrégat, soit d'au moins un constructeur constexpr ou un modèle de constructeur (éventuellement hérité d'une classe de base) qui n'est pas un constructeur de copie ou de déplacement,

(10.5.3) - s'il s'agit d'une union, au moins un de ses membres de données non statiques est de type littéral non volatile

(10.5.4) - s'il ne s'agit pas d'une union, tous ses membres de données non statiques et ses classes de base sont de types littéraux non volatils.

Ques 1: Pourquoi un destructeur ne peut pas être marqué comme constexpr?

Parce que seuls les destructeurs triviaux sont qualifiés pour constexpr Voici la section pertinente du draft

Un destructeur est trivial s'il n'est pas fourni par l'utilisateur et si:

(5.4) - le destructeur n'est pas virtuel,

(5.5) - toutes les classes de base directes de sa classe ont des destructeurs triviaux, et

(5.6) - pour tous les membres de données non statiques de sa classe qui sont de type classe (ou tableau de ceux-ci), chacune de ces classes a un destructeur trivial.

Sinon, le destructeur n'est pas trivial.

Ques 2: Si je ne fournis pas de destructeur, le destructeur généré implicitement est-il constexpr?

Oui, car le destructeur généré implicitement est de type trivial, il est donc qualifié pour constexpr

Ques 3: Si je déclare un destructeur par défaut (~ X () = default;), est-ce automatiquement constexpr?

En effet, ce destructeur est déclaré par l'utilisateur et généré implicitement et il est donc qualifié pour constexpr.


Je ne suis pas en mesure de trouver une référence directe que seuls _ triviaux destructors sont qualifiés pour constexpr mais si le destructeur n'est pas trivial, il est certain que le type de classe n'est pas cv-qualified. Donc, c'est un peu implicite car vous ne pouvez pas définir un destructor pour cv-qualified classe.


Mise à jour C++ 20

Depuis C++ 20, les destructeurs définis par l'utilisateur peuvent également être constexpr sous certaines conditions.

dcl.constexpr/ :

La définition d'une fonction constexpr doit satisfaire aux exigences suivantes:

  • son type de retour (le cas échéant) doit être un type littéral;
  • chacun de ses types de paramètres doit être un type littéral;
  • ce ne doit pas être une coroutine ([dcl.fct.def.coroutine]);
  • si la fonction est un constructeur ou un destructeur, sa classe ne doit avoir aucune classe de base virtuelle;
  • son corps de fonction ne doit pas contenir ([stmt.pre])
    • une déclaration goto,
    • un identifiant ([stmt.label]),
    • une définition d'une variable de type non littéral ou de durée de stockage statique ou de thread.
14
Hemant Gangwar

Si ce que vous recherchez est le raisonnement derrière la restriction, jetez un œil à ce document qui indique clairement que la restriction est artificielle - il n'y a aucune propriété intrinsèque des destructeurs qui les empêche de travailler dans des contextes constexpr, et en effet les implémenteurs du compilateur conviennent que les supporter dans des contextes constexpr sera trivial à implémenter .

Je suppose que le comité des normes C++ a initialement placé la restriction dans C++ 11 parce qu'il ne voulait pas traiter avec les destructeurs à ce moment-là et il était plus facile de les exclure complètement.

5
saarraz1

Pourquoi un destructeur ne peut pas être marqué comme constexpr?

La norme C++ 11 est spécifique à l'utilisation de constexpr pour les constructeurs et les fonctions membres non statiques. Il ne dit rien de spécifique sur le destructeur. On peut supposer que les destructeurs doivent être traités comme des fonctions membres non statiques.

constexpr ne peut être utilisé que pour les fonctions membres const. Puisqu'un destructeur ne peut pas être une fonction membre const, il ne peut pas être qualifié de fonction membre constexpr.

Si je ne fournis pas de destructeur, c'est le destructeur généré implicitement constexpr.

Depuis l'utilisation de

constexpr ~X() = default;

est une erreur, il est logique pour moi que le destructeur généré par le compilateur ne soit pas une fonction constexpr. Je ne trouve rien dans la norme pour justifier ma déclaration. Je devine.

Si je déclare un destructeur par défaut (~X() = default;), est-ce automatiquement constexpr

Je crois que non. Encore une fois, je ne trouve rien dans la norme pour justifier ma déclaration. Je devine.


FWIW, g ++ compile et construit très bien le programme suivant.

struct X {
   constexpr X(int i) : i_(i) {}
   ~X() = default;
   int i_;
};

int main()
{
   const X x(10);
}
4
R Sahu

Un destructeur ne peut pas être constexpr car les fonctions constexpr ne peuvent pas avoir d'effets secondaires et les destructeurs par définition ne sont utiles que par le biais d'effets secondaires. Bref, il serait inutile d'avoir un destructeur qui est constexpr.

Un objet ne peut pas être constexpr si son destructeur n'est pas trivial. Une valeur par défaut, si triviale, sera considérée comme constexpr

en direct

De [class.dtor]

Chaque spécificateur décl de la spécification décl-seq d'une déclaration de destructeur (le cas échéant) doit être friend, inline ou virtual.

Manquant, constexpr. Donc, vous pouvez simplement le prendre comme suit: parce que la norme le ditTM

3
Passer By

Référence disons:

destructeurs constexpr

Dans la plupart des cas, pour créer un objet de type T dans une expression constante, la destruction de T doit être triviale. Cependant, les destructeurs non triviaux sont un composant important du C++ moderne, en partie en raison de l'utilisation généralisée de l'idiome RAII, qui est également applicable dans les évaluations constexpr. Les destructeurs non triviaux peuvent être pris en charge dans les expressions constantes, comme suit:

  • Autoriser les destructeurs à être marqués comme constexpr
  • Rendre les destructeurs par défaut constexpr s'ils invoquent uniquement les destructeurs constexpr
  • Pour les variables constexpr, exiger que l'évaluation du destructeur soit une expression constante (sauf que l'objet détruit peut être modifié dans son propre destructeur

Cependant, aucun cas d'utilisation convaincant n'est connu pour une telle fonctionnalité, et il y aurait un coût d'implémentation non trivial garantissant que les destructeurs sont exécutés au bon moment.

2
msc

Depuis C++ 20, un constructeur peut être marqué constexpr; Je ne sais pas si cela dit quelque part spécifiquement "un destructeur peut être constexpr", mais le projet de norme inclut le texte suivant dans la section 9.2.5 paragraphe 5:

La définition d'un destructeur constexpr dont le corps de fonction n'est pas = delete doit en outre satisfaire à l'exigence suivante:

  • pour chaque sous-objet du type de classe ou de son tableau (éventuellement multidimensionnel), ce type de classe doit avoir un destructeur constexpr.

Cela a également une fonction utile car C++ 20 permet également new et delete dans les contextes constexpr, ce qui permet des choses comme vector et string pour travailler à la compilation sans hacks (bien que je pense que C++ 20 n'inclut pas réellement les modifications de la bibliothèque standard pour permettre cela, il est possible d'implémenter quelque chose avec la même API et le même comportement que vector qui fonctionne complètement au moment de la compilation).

1
Cubic