46 votes

<span class="translated">C++ ordre de destruction de std::tuple</span>

Y a-t-il une règle qui stipule dans quel ordre les membres d'un std::tuple sont détruits?

Par exemple, si Function1 retourne un std::tuple, std::unique_ptr> à Function2, puis-je être sûr que (quand la portée de Function2 est quittée) l'instance de ClassB référencée par le deuxième membre est détruite avant l'instance de ClassA référencée par le premier membre?

std::tuple< std::unique_ptr< ClassA >, std::unique_ptr< ClassB > > Function1()
{
    std::tuple< std::unique_ptr< ClassA >, std::unique_ptr< ClassB > > garbage;
    get<0>(garbage).reset( /* ... */ );
    get<1>(garbage).reset( /* ... */ );
    return garbage;
}

void Function2()
{
    auto to_be_destroyed = Function1();
    // ... faire autre chose

    // to_be_destroyed quitte la portée
    // L'instance de ClassB est-elle détruite avant l'instance de ClassA?
}

53voto

einpoklum Points 2893

Je vais offrir une leçon de vie que j'ai apprise, plutôt qu'une réponse directe, en réponse à votre question :

Si vous pouvez formuler, pour plusieurs alternatives, un argument raisonnable pour expliquer pourquoi cette alternative devrait être celle prescrite par la norme - alors vous ne devriez pas supposer aucune d'entre elles n'est prescrite (même si l'une d'entre elles se trouve être).

Dans le contexte des tuples - s'il vous plaît, soyez gentil avec les personnes qui maintiennent votre code et ne permettez pas que l'ordre de destruction des éléments du tuple puisse perturber la destruction d'autres éléments. C'est tout simplement maléfique... imaginez le programmeur malheureux qui devra déboguer cette chose. En fait, ce pauvre individu pourrait être vous-même dans quelques années, lorsque vous aurez déjà oublié votre astuce astucieuse d'antan.

Si vous devez absolument compter sur l'ordre de destruction, peut-être devriez-vous simplement utiliser une classe appropriée avec les éléments du tuple en tant que membres de données (pour laquelle vous pourriez écrire un destructeur, afin de préciser ce qui doit se passer dans quel ordre), ou un autre agencement facilitant un contrôle plus explicite de la destruction.

35voto

40two Points 8224

La norme ne spécifie pas l'ordre de destruction pour std::tuple. Le fait que §20.4.1/p1 spécifie que:

Une instantiation de tuple avec deux arguments est similaire à une instantiation de pair avec les mêmes deux arguments.

Similaire ici n'est pas interprété comme identique et par conséquent, il n'est pas implicite que std::tuple devrait avoir un ordre de destruction inversé de ses arguments.

Étant donné la nature récursive de std::tuple, il est très probable que l'ordre de destruction soit dans l'ordre de ses arguments.

Je base également mes hypothèses sur un rapport de bug pour BUG GCC 66699 où dans la discussion mes hypothèses ci-dessus sont justifiées.

Cela dit, l'ordre de destruction pour std::tuple n'est pas spécifié.

13voto

Arunmu Points 2111

Avec Clang 3.4, j'obtiens le même ordre de destruction pour à la fois std::pair et std::tuple à 2 éléments, et avec g++ 5.3 j'obtiens un ordre opposé qui pourrait être principalement dû à l'implémentation récursive de std::tuple dans libstd++.

Donc, cela revient essentiellement à ce que j'ai dit dans le commentaire, c'est une définition spécifique à l'implémentation.

Dans le rapport de BUG :

Commentaire de Martin Sebor

Étant donné que la disposition des membres de std::pair est entièrement spécifiée, l'ordre de leur initialisation et destruction l'est également. La sortie du cas de test reflète cet ordre.

L'ordre d'initialisation (et de destruction) des sous-objets std:stuple est moins clairement spécifié. Il n'est pas immédiatement évident de ma lecture de la spécification s'il est nécessaire de respecter un ordre particulier.

La raison pour laquelle la sortie pour std::tuple avec libstdc++ est à l'envers de std::pair est parce que l'implémentation, qui repose sur l'héritage récursif, stocke et construit les éléments de tuple dans l'ordre inverse : c'est-à-dire, la classe de base, qui stocke le dernier élément, est stockée et construite en premier, suivie de chaque classe dérivée (chacune stockant le dernier élément - Nème).

La citation de la norme [section 20.4.1] à laquelle le rapporteur de bug fait référence :

1 Ce sous-clause décrit la bibliothèque de tuple qui fournit un type tuple en tant que modèle de classe tuple qui peut être instancié avec un nombre quelconque d'arguments. Chaque argument du modèle spécifie le type d'un élément dans le tuple. Par conséquent, les tuples sont des collections hétérogènes de valeurs de taille fixe. Une instantiation de tuple avec deux arguments est similaire à une instantiation de pair avec les mêmes deux arguments. Voir 20.3.

L'argument contre cela fait dans le bug lié est :

Être décrit comme similaire n'implique pas qu'ils sont identiques dans tous les détails. std::pair et std::tuple sont des classes distinctes avec des exigences différentes pour chacune. Si vous croyez qu'elles sont censées se comporter de manière identique à cet égard (c'est-à-dire, avoir leurs sous-objets définis dans le même ordre), vous devez indiquer les termes spécifiques qui le garantissent.

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