web-dev-qa-db-fra.com

Destruction de la valeur de retour sur l'exception du destructeur

J'ai le code suivant:

#include <stdexcept>
#include <iostream>

struct ok {
    int _n;
    ok(int n) : _n(n) { std::cerr << "OK" << n << " born" << std::endl; }
    ~ok() {  std::cerr << "OK" << _n << " gone" << std::endl; }
};

struct problematic {
    ~problematic() noexcept(false) { throw std::logic_error("d-tor exception"); }
};

ok boo() {
    ok ok1{1};
    problematic p;
    ok ok2{2};
    return ok{3}; // Only constructor is called...
}

int main(int argc, char **argv) {
    try {boo();} catch(...) {}
}

Je vois que le destructeur de ok {3} n'est pas appelé, la sortie est:

 OK1 born
 OK2 born
 OK3 born
 OK2 gone
 OK1 gone

Est-ce le comportement attendu pour C++ 14?

MODIFIER:

Compiler avec gcc 6.3

38
Evgeny

Selon la norme, ce comportement est incorrect et cela a déjà été mentionné dans la section des commentaires de la question. Ceci est indiqué dans la section Gestion des exceptions .

Selon les rapports de défauts sur open-std.org , ils étaient conscients que les implémentations (GCC et Clang) étaient erronées à ce sujet dès le 2015-09-28. Mais la résolution proposée n'était qu'en février 2016 et les compilateurs (GCC et Clang) n'ont pas encore inclus ce correctif.

Résolution proposée (février 2016):

Modifier le paragraphe 2 de 18.2 [except.ctor] comme suit:
Le destructeur est invoqué pour chaque objet automatique de type classe construit, mais pas encore détruit, depuis l'entrée du bloc try. Si une exception est levée lors de la destruction de variables temporaires ou locales pour une instruction return (9.6.3 [stmt.return]), le destructeur de l'objet retourné (le cas échéant) est également appelé . Les objets sont détruits dans l'ordre inverse de l'achèvement de leur construction. [Exemple:

  struct A { };

  struct Y { ~Y() noexcept(false) { throw 0; } };

  A f() {
    try {
      A a;
      Y y;
      A b;
      return {};   // #1
    } catch (...) {
    }
    return {};     // #2
  }

Au # 1, l'objet retourné de type A est construit. Ensuite, la variable locale b est détruite (9.6 [stmt.jump]). Ensuite, la variable locale y est détruite, provoquant le déroulement de la pile, entraînant la destruction de l'objet retourné, suivie de la destruction de la variable locale a. Enfin, l'objet retourné est reconstruit à # 2. —Fin exemple]

Des bogues ont été déposés contre ce problème à la fois dans GCC et Clang .

Les commentaires sur le rapport de bogue de GCC indiquent qu'il s'agit clairement d'un bogue.

Jonathan Wakely commentaires:

C'est maintenant 2013, donc la chose sensée à faire n'est pas de revenir en valeur si votre destructeur peut lancer.

Et un autre utilisateur:

Oui, je l'ai remarqué, et Clang a également eu un bug déposé contre eux qui languit depuis des années. Néanmoins, le comportement est incorrect.

23
P.W