49 votes

Pourquoi devons-nous utiliser virtual ~A() = default ; au lieu de virtual ~A() {} en C++11 ?

Dans le post de Stack Overflow Vérification du type d'objet en C++11 j'ai le commentaire :

En C++11, vous voudrez en fait faire virtual ~A() = default; Sinon, vous perdrez les constructeurs implicites de déplacement.

Qu'est-ce que virtual ~A() = default; pour ? Comment se fait-il que les constructeurs implicites de déplacement perdent avec virtual ~A() {} ?

42voto

Howard Hinnant Points 59526

Le commentaire n'est pas correct.

Les deux :

virtual ~A() = default;

et

virtual ~A() {}

sont des utilisateurs déclaré . Et les membres implicitement déplacés sont inhibés si le destructeur est déclaré par l'utilisateur.

[dcl.fct.def.default]/p4 discussions déclaré par l'utilisateur y fourni par l'utilisateur membres spéciaux :

Une fonction membre spéciale est fourni par l'utilisateur si elle est déclarée par l'utilisateur et et qu'il n'a pas été explicitement défini par défaut ou supprimé lors de sa première déclaration.

27voto

Dans ce poste https://stackoverflow.com/a/17204598/260127 j'ai le commentaire :

En C++11, vous voudrez en fait faire virtual ~A() = default; Sinon, vous perdrez les constructeurs implicites de déplacement.

Le commentaire est incorrect.

Même default ed, ce destructeur est " déclaré par l'utilisateur "(mais notez que ce n'est pas aussi " fourni par l'utilisateur ").

#include <iostream>

struct Helper
{
    Helper() {}
    Helper(const Helper& src) { std::cout << "copy\n"; }
    Helper(Helper&& src)      { std::cout << "move\n"; }
};

struct A
{
    virtual ~A() {}
    Helper h;
};

struct B
{
    virtual ~B() = default;
    Helper h;
};

struct C
{
    Helper h;
};

int main()
{
    {
        A x;
        A y(std::move(x));   // outputs "copy", because no move possible
    }

    {
        B x;
        B y(std::move(x));   // outputs "copy", because still no move possible
    }

    {
        C x;
        C y(std::move(x));   // outputs "move", because no user-declared dtor
    } 
}

Démonstration en direct :

+ g++-4.8 -std=c++11 -O2 -Wall -pthread main.cpp
+ ./a.out
copie
copie
déplacer

Vous n'avez donc rien "perdu" - il n'y avait pas de fonctionnalité de déplacement au départ !

Voici le passage standard qui interdit un constructeur implicite de déplacement en les deux cas :

[C++11: 12.8/9]: Si la définition d'une classe X ne déclare pas explicitement un constructeur de déplacement, un constructeur sera implicitement déclaré par défaut si et seulement si

  • X n'a pas de constructeur de copie déclaré par l'utilisateur,
  • X n'a pas d'opérateur d'affectation de copie déclaré par l'utilisateur,
  • X n'a pas d'opérateur d'affectation de déplacement déclaré par l'utilisateur,
  • X n'a pas de destructeur déclaré par l'utilisateur et
  • le constructeur move ne serait pas implicitement défini comme supprimé.

Bootnote

Cela ne ferait pas de mal si une future version de la norme donnait la signification précise de termes tels que "déclaré par l'utilisateur". Il y a, au moins, ceci :

[C++11: 8.4.2/4]: [..] Une fonction membre spéciale est fourni par l'utilisateur s'il est déclaré par l'utilisateur et n'est pas explicitement défini par défaut ou supprimé lors de sa première déclaration. [..]

On peut supposer que la distinction est ici implicite.

5voto

legends2k Points 6380

Ce commentaire est faux.

Au lieu de fournir votre propre constructeur de déplacement, si vous voulez que le compilateur en fournisse un, l'une des exigences est qu'il s'attende à ce que le destructeur soit également fourni par lui, c'est-à-dire un destructeur trivial. Cependant, la norme actuelle est assez stricte sur le moment où une implémentation implicite peut être fournie - en acceptant la façon dont un destructeur est donné par l'utilisateur. Tout ce qui est déclaré par l'utilisateur est considéré comme une prise en main par l'utilisateur, et donc non seulement cette implémentation implicite mais aussi l'implémentation implicite du destructeur.

~A() { … }

mais aussi ceci

~A() = default;

fait que le compilateur ne fournit pas un destructeur implicite. La première est une définition et donc aussi une déclaration ; la seconde est juste une déclaration. Dans les deux cas, le destructeur est déclaré par l'utilisateur et interdit donc au compilateur de fournir un constructeur implicite de déplacement.

Je suppose que le raisonnement derrière cette exigence est que pendant déplacer les ressources d'un objet sont déplacées vers un autre objet, laissant l'objet original dans un état où il n'a pas de ressources en stockage dynamique ; mais si votre classe n'a pas de telles ressources, elle peut être déplacée, détruite, etc. de manière triviale. Lorsque vous déclarez un destructeur non trivial, c'est un signal pour le compilateur que les ressources que vous gérez dans la classe ne sont pas quelque chose de trivial et que vous devrez surtout fournir des ressources non triviales. déplacer aussi, donc le compilateur n'en fournit pas.

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