83 votes

Enum bizarre dans le destructeur

Actuellement, je suis en train de lire le code source de Protocol Buffer et j'en ai trouvé une bizarre enum codes définis aquí

  ~scoped_ptr() {
    enum { type_must_be_complete = sizeof(C) };
    delete ptr_;
  }

  void reset(C* p = NULL) {
    if (p != ptr_) {
      enum { type_must_be_complete = sizeof(C) };
      delete ptr_;
      ptr_ = p;
    }
  }

Pourquoi le enum { type_must_be_complete = sizeof(C) }; est définie ici ? à quoi sert-elle ?

81voto

Mohit Jain Points 6202

Cette astuce évite l'UB en s'assurant que la définition de C est disponible lorsque ce destructeur est compilé. Dans le cas contraire, la compilation échouerait car le sizeof Le type incomplet (types déclarés en avant) ne peut pas être déterminé mais les pointeurs peuvent être utilisés.

Dans un binaire compilé, ce code serait optimisé et n'aurait aucun effet.

Notez que : La suppression d'un type incomplet peut être un comportement non défini. de 5.3.5/5 :.

si l'objet à supprimer a classe incomplète au point de suppression et la classe complète a un destructeur non trivial ou un fonction de désaffectation, la le comportement est indéfini .

g++ émet même l'avertissement suivant :

avertissement : un problème possible a été détecté dans l'invocation de delete de l'opérateur :
avertissement : 'p' a un type incomplet
avertissement : déclaration anticipée déclaration de 'struct C

31voto

Bathsheba Points 23209

sizeof(C) échouera à temps de compilation si C n'est pas un type complet. Définition d'une portée locale enum rend la déclaration inoffensive au moment de l'exécution.

C'est une manière pour le programmeur de se protéger de lui-même : le comportement d'un prochain delete ptr_ sur un type incomplet est indéfini s'il a un destructeur non trivial.

28voto

Pour comprendre le enum pour commencer à considérer le destructeur sans elle :

~scoped_ptr() {
    delete ptr_;
}

ptr_ es un C* . Si le type C est incomplet à ce stade, c'est-à-dire que tout ce que le compilateur sait est struct C; entonces (1) a généré par défaut le destructeur do-nothing est utilisé pour l'instance C pointée. Il est peu probable que ce soit la bonne chose à faire pour un objet géré par un pointeur intelligent.

Si la suppression via un pointeur vers un type incomplet avait toujours eu un comportement indéfini, alors la norme pourrait simplement exiger que le compilateur le diagnostique et échoue. Mais c'est bien défini quand le vrai destructeur est trivial : une connaissance que le programmeur peut avoir, mais que le compilateur n'a pas. Je ne sais pas pourquoi le langage définit et permet cela, mais le C++ supporte de nombreuses pratiques qui ne sont pas considérées aujourd'hui comme des bonnes pratiques.

Un type complet a une taille connue, et donc, sizeof(C) compilera si et seulement si C est un type complet -- avec un destructeur connu. Il peut donc être utilisé comme garde. Une façon de faire serait de simplement

(void) sizeof(C);  // Type must be complete

Je voudrais devinez qu'avec un certain compilateur et certaines options, le compilateur l'optimise avant même qu'il puisse remarquer qu'il ne devrait pas compiler, et que le enum est un moyen d'éviter un tel comportement non conforme du compilateur :

enum { type_must_be_complete = sizeof(C) };

Une explication alternative pour le choix de enum plutôt qu'une simple expression abandonnée, est simplement une préférence personnelle.

Ou comme le suggère James T. Hugget dans un commentaire à cette réponse, "L'enum peut être un moyen de créer un message d'erreur pseudo-portable au moment de la compilation".


(1) Le destructeur do-nothing généré par défaut pour un type incomplet posait un problème avec les anciennes versions de l'outil. std::auto_ptr . C'était si insidieux qu'il a fait son chemin dans un article de GOTW sur l'idiome PIMPL écrit par le président du comité international de normalisation du C++, Herb Sutter. Bien sûr, de nos jours std::auto_ptr est déprécié, on utilisera plutôt un autre mécanisme.

3voto

Jerome Points 499

Peut-être une astuce pour être sûr C est définie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X