pourquoi supprimer explicitement le constructeur?

93

Quand / pourquoi voudrais-je supprimer explicitement mon constructeur? En supposant que la raison est d'empêcher son utilisation, pourquoi ne pas simplement le faire private?

class Foo
{ 
  public: 
    Foo() = delete; 
};
cendre
la source
14
Cela va bien avec = default, même la classe ne peut pas l'utiliser, et je préfère personnellement voir Utilisation de la fonction supprimée. over La fonction est privée. Le premier stipule explicitement que «ceci n'est pas destiné à être utilisé». Si quelque chose sort de cela, le fait de ne pas pouvoir l'utiliser fait en fait une différence sémantique.
chris
15
Je pense honnêtement que les gens commencent à devenir agressifs avec les votes serrés. Je ne vois pas en quoi ce n'est pas constructif.
Luchian Grigore
4
@LuchianGrigore: D'accord. Je me demande pourquoi la communauté est devenue moi-même beaucoup plus rigide. Je ne vois pas l'intérêt.
Ed S.
11
Comme j'utilise rarement C ++ 11, c'est plus informatif pour moi que l'OP ne le réalise probablement. Je ne savais même pas que vous pouvez marquer un constructeur pour delete. La question et la réponse de Luchian peuvent être facilement qualifiées de constructives. Quiconque ne respire pas les subtilités de C ++ 11 mais en aura besoin bientôt obtiendra quelque chose des deux.
WhozCraig

Réponses:

87

Que diriez-vous:

//deleted constructor
class Foo
{ 
  public: 
    Foo() = delete;     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //illegal
}

contre

//private constructor
class Foo
{ 
  private: 
    Foo() {}     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //legal
}

Ce sont des choses fondamentalement différentes. privatevous indique que seuls les membres de la classe peuvent appeler cette méthode ou accéder à cette variable (ou à des amis bien sûr). Dans ce cas, il est légal pour une staticméthode de cette classe (ou de tout autre membre) d'appeler un privateconstructeur d'une classe. Cela ne vaut pas pour les constructeurs supprimés.

Échantillon ici .

Luchian Grigore
la source
3
Vous n'avez pas du tout besoin de déclarer Foo () si vous déclarez Foo (int). Foo () ne sera pas généré et donc Foo f est de toute façon invalide. Ainsi, votre exemple ne montre pas le cas du constructeur supprimé. Voyez par vous-même - ideone.com/mogiIF
marquez
1
@mark J'ai écrit 2 constructeurs pour prouver le point. Je vais éditer pour que tout le monde soit clair.
Luchian Grigore
1
Je comprends la différence, je ne comprends tout simplement pas la valeur ajoutée de l'instruction delete en général et pour un constructeur en particulier. Après tout, je pourrais spécifier un constructeur par défaut privé sans le corps. Ensuite, le code échoue également, uniquement pendant la liaison. Eh bien, je peux voir que la suppression transmet l'intention plus explicitement, mais c'est à peu près tout.
marque
11
@mark Oui, ce serait la manière C ++ 98 de faire les choses. Mais à mon humble avis, transmettre clairement l'intention est en fait une chose très importante dans la programmation en général. Dans ce cas, certains lecteurs peuvent voir un constructeur privé non défini et supposer qu'il est accidentel et ajouter simplement une définition pour celui-ci, surtout si la définition est aussi triviale qu'un constructeur par défaut (Oui, avoir un commentaire aide, mais nous préférerions de loin le compilateur -enforcement sur comment-application). En ayant notre intention plus claire, nous obtenons également un bien meilleur message d'erreur qui dit «explicitement supprimé» plutôt que «référence non définie».
mpark
2
Honnêtement, je ne comprends pas comment cela répond à la question principale. La question dans le titre et la première question de OP dans le message était: Quand / pourquoi voudrais-je supprimer explicitement mon constructeur?
Alexander Bolinsky
11

pourquoi supprimer explicitement le constructeur?

Autre raison:
j'utilise deletequand je veux m'assurer qu'une classe est appelée avec un initialiseur. Je considère cela comme un moyen très élégant d'y parvenir sans contrôle d'exécution.

Le compilateur C ++ effectue cette vérification pour vous.

class Foo
{
   public:
       Foo() = delete;
       Foo(int bar) : m_bar(bar) {};
   private:
       int m_bar;
}

Ce code - très simplifié - garantit qu'il n'y a pas d'instanciation comme celle-ci:Foo foo;

Peter VARGA
la source
12
La déclaration supprimée est ici inutile. Il est automatiquement supprimé avec n'importe quel constructeur fourni par l'utilisateur
Mike Lui
5
Pour clarifier le commentaire de @MikeLui, la déclaration supprimée est inutile pour le compilateur . Il existe de nombreux cas où un code comme celui-ci doit être inclus pour déclarer l'intention à d'autres programmeurs .
Jeff G
En plus de déclarer votre intention, cela crée un endroit évident pour documenter la raison de sa suppression dans l'interface publique, et en plus l'erreur du compilateur sera quelque chose de court comme "utilisation d'une fonction supprimée". S'il y Fooavait de nombreux constructeurs, mais pas un par défaut, Foo foo;cela provoquerait une erreur beaucoup plus longue listant tous les constructeurs implicitement définis, protégés et privés auxquels il ne correspond pas.
sigma
Je ne comprends toujours pas comment une ligne supplémentaire avec la déclaration du constructeur dont le mot-clé "= delete" déclare l'intention de l'idée "pas de constructeur par défaut" mieux que ... juste pas de constructeur par défaut? Exemple: je ne veux pas déclarer la variable "a" dans mon code - quoi de mieux, écrire "// int a; // pas besoin de définir la variable a" ou simplement ne rien écrire sur cette variable dans le code?
Ezh
2

J'ai rencontré des ctors par défaut déclarés comme «supprimés» dans le code source de LLVM (dans AlignOf.h par exemple). Les modèles de classe associés sont généralement dans un espace de noms spécial appelé «llvm :: detail». Le but ici, je pense, était qu'ils considéraient cette classe uniquement comme une classe d'aide. Ils n'ont jamais eu l'intention de les instancier; uniquement pour les utiliser dans le contexte d'autres modèles de classe avec quelques astuces de métaprogrammation qui s'exécutent au moment de la compilation.

Par exemple. il y a ce modèle de classe AlignmentCalcImpl qui est utilisé uniquement dans un autre modèle de classe appelé AlignOf en tant que paramètre de l'opérateur sizeof (.). Cette expression peut être évaluée au moment de la compilation; et il n'est pas nécessaire d'instancier le modèle -> alors pourquoi ne pas déclarer le ctor delete par défaut pour exprimer cette intention.

Mais ce n'est que mon hypothèse.

gybacsi
la source