J'ai initialement posté cela comme une question uniquement sur les destructeurs, mais maintenant j'ajoute la considération du constructeur par défaut. Voici la question originale:
Si je veux donner à ma classe un destructeur qui est virtuel, mais qui est sinon le même que ce que le compilateur générerait, je peux utiliser
=default
:class Widget { public: virtual ~Widget() = default; };
Mais il semble que je puisse obtenir le même effet avec moins de frappe en utilisant une définition vide:
class Widget { public: virtual ~Widget() {} };
Y a-t-il une manière dont ces deux définitions se comportent différemment?
D'après les réponses publiées pour cette question, la situation du constructeur par défaut semble similaire. Étant donné qu'il n'y a presque aucune différence de signification entre " =default
" et " {}
" pour les destructeurs, n'y a-t-il pas de différence de signification similaire entre ces options pour les constructeurs par défaut? Autrement dit, en supposant que je veuille créer un type où les objets de ce type seront à la fois créés et détruits, pourquoi voudrais-je dire
Widget() = default;
au lieu de
Widget() {}
?
Je m'excuse si l'extension de cette question après sa publication d'origine enfreint certaines règles SO. Publier une question presque identique pour les constructeurs par défaut m'a semblé être l'option la moins souhaitable.
la source
= default
est imo plus explicite, et est cohérent avec le support pour cela avec les constructeurs.std::has_trivial_destructor<Widget>::value
va de mêmetrue
pour le premier, maisfalse
pour le second. Quelles sont les implications de cela, je ne sais pas non plus. :)Réponses:
C'est une question complètement différente lorsqu'on pose des questions sur les constructeurs et sur les destructeurs.
Si votre destructeur l'est
virtual
, alors la différence est négligeable, comme l'a souligné Howard . Cependant, si votre destructeur n'était pas virtuel , c'est une histoire complètement différente. Il en va de même pour les constructeurs.Utiliser la
= default
syntaxe pour les fonctions membres spéciales (constructeur par défaut, copier / déplacer les constructeurs / affectation, destructeurs, etc.) signifie quelque chose de très différent de simplement faire{}
. Avec ce dernier, la fonction devient "fournie par l'utilisateur". Et cela change tout.C'est une classe triviale selon la définition de C ++ 11:
Si vous essayez d'en construire un par défaut, le compilateur générera automatiquement un constructeur par défaut. Il en va de même pour la copie / mouvement et la destruction. Étant donné que l'utilisateur n'a fourni aucune de ces fonctions membres, la spécification C ++ 11 considère qu'il s'agit d'une classe «triviale». Il est donc légal de faire cela, comme mémoriser leur contenu pour les initialiser et ainsi de suite.
Ce:
Comme son nom l'indique, ce n'est plus anodin. Il a un constructeur par défaut fourni par l'utilisateur. Peu importe qu'il soit vide; en ce qui concerne les règles de C ++ 11, cela ne peut pas être un type trivial.
Ce:
Encore une fois, comme son nom l'indique, il s'agit d'un type trivial. Pourquoi? Parce que vous avez dit au compilateur de générer automatiquement le constructeur par défaut. Le constructeur n'est donc pas "fourni par l'utilisateur". Et par conséquent, le type compte comme trivial, car il n'a pas de constructeur par défaut fourni par l'utilisateur.
La
= default
syntaxe est principalement là pour faire des choses comme la copie de constructeurs / affectation, lorsque vous ajoutez des fonctions membres qui empêchent la création de telles fonctions. Mais cela déclenche également un comportement spécial du compilateur, donc il est également utile dans les constructeurs / destructeurs par défaut.la source
=default
fonctions) et des fonctions fournies par l'utilisateur (ce qui est le cas pour{}
). Les fonctions déclarées par l'utilisateur et fournies par l'utilisateur peuvent empêcher la génération d'autres fonctions membres spéciales (par exemple, un destructeur déclaré par l'utilisateur empêche la génération des opérations de déplacement), mais seule une fonction spéciale fournie par l'utilisateur rend une classe non triviale. Droite?= default
semble être utile pour forcer le compilateur à générer un constructeur par défaut malgré la présence d'autres constructeurs; le constructeur par défaut n'est pas déclaré implicitement si d'autres constructeurs déclarés par l'utilisateur sont fournis.Ils sont tous les deux non triviaux.
Ils ont tous deux la même spécification noexcept en fonction de la spécification noexcept des bases et des membres.
La seule différence que je détecte jusqu'à présent est que si
Widget
contient une base ou un membre avec un destructeur inaccessible ou supprimé:Ensuite, la
=default
solution se compilera, maisWidget
ne sera pas de type destructible. C'est-à-dire que si vous essayez de détruire unWidget
, vous obtiendrez une erreur de compilation. Mais si vous ne le faites pas, vous avez un programme qui fonctionne.Otoh, si vous fournissez le destructeur fourni par l' utilisateur , les choses ne se compileront pas, que vous détruisiez ou non un
Widget
:la source
=default;
le compilateur ne générera pas le destructeur à moins qu'il ne soit utilisé, et ne déclenchera donc pas d'erreur. Cela me semble bizarre, même si ce n'est pas forcément un bug. Je ne peux pas imaginer que ce comportement soit obligatoire dans la norme.La différence importante entre
et
est que le constructeur par défaut défini avec
B() = default;
est considéré comme non défini par l'utilisateur . Cela signifie qu'en cas d' initialisation de valeur comme dansun type spécial d'initialisation qui n'utilise pas du tout de constructeur aura lieu et pour les types intégrés, cela entraînera une initialisation à zéro . Dans le cas où
B(){}
cela n'aura pas lieu. La norme C ++ n3337 § 8.5 / 7 ditPar exemple:
résultat possible:
http://ideone.com/k8mBrd
la source