45
votes

Quelle est la différence entre le constructeur "= default" et le constructeur généré par le compilateur en C ++?

Exemple de code:

class Dog
{
private:
    int x;
};

vs. Ce code:

class Dog
{
private:
    int x;
public:
    Dog()=default;
};

Quelle est la différence entre le constructeur qui est "= par défaut" (le premier code) et le constructeur que le compilateur crée (comme le deuxième code)?


4 commentaires

Au moins une différence: dans C ++ 20, avoir un constructeur dénué par l'utilisateur (même si vous utilisez l'implémentation par défaut) signifie que votre classe ne compte plus comme un agrégat.


Voulez-vous connaître la différence entre les deux constructeurs (comme le titre l'indique) ou la raison de l'utilisation de = default (comme le texte l'indique).


De quelle version de C ++ vous préoccupez-vous? Différentes versions de la norme ont traité le comportement différemment


Est-ce que cela répond à votre question? la nouvelle syntaxe "= par défaut" en C ++ 11


3 Réponses :


3
votes
  • Les constructeurs déclarés par défaut peuvent être gardés par un accès protégé ou privé. Les constructeurs par défaut non déclarés sont toujours des membres publics en ligne de leurs classes.
  • = Default Les constructeurs sont instanciés comme les fonctions membres habituelles, peuvent être en ligne ou non, ils ont des références de liaison solides dans le deuxième cas. Le compilateur crée des constructeurs par défaut non déclarés comme fonctions membres en ligne avec des références de liaison faibles.
  • Comme mentionné dans les commentaires, = par défaut Les constructeurs sont des constructeurs définis par l'utilisateur et leurs classes ne sont pas des types agrégés.

  • 6 commentaires

    Cela ne répond pas vraiment à la question de savoir s'il y a une différence entre les deux définitions de classe.


    Y a-t-il une telle question? Quelle est la différence entre le constructeur qui est = par défaut (le premier code) au constructeur que le compilateur crée (comme le deuxième code) Je n'y arrive pas différence entre les deux définitions de classe < / b>.


    Intéressant: comment pouvez-vous rendre le constructeur = par défaut non en ligne?


    @ Peter-ReinstateMonica Déclarez dans la classe sans = default , puis ajoutez = default dans la définition. Il lui fait perdre ses propriétés magiques (par exemple, l'initialisation de la valeur se comportant différemment de l'initialisation de la valeur).


    @Holyblackcat Je ne savais pas que c'était possible. Cela explique pourquoi un constructeur par défaut par défaut ne se distingue pas des autres constructeurs définis par l'utilisateur et modifie les propriétés de la classe en conséquence: le compilateur ne peut pas savoir dans le cas général comment le constructeur défini par l'utilisateur est implémenté.


    @ Peter-ReinstateMonica Pour être clair, s'il est = default ED dans le corps de la classe, il obtient des propriétés magiques (identiques au ctor généré par le compilateur). Si c'est = default ED à l'extérieur, il les perd.



    40
    votes

    dog () = par défaut; est un constructeur déclaré utilisateur (à ne pas confondre avec un constructeur défini par l'utilisateur). Il s'agit d'un constructeur par défaut par défaut. En règle générale, vous l'utiliseriez lorsque la classe a d'autres constructeurs, mais vous voulez toujours que le compilateur génére un constructeur par défaut (ou plutôt un "constructeur par défaut par défaut". Il s'agit de la terminologie C ++ à son meilleur. signification différente).

    Un constructeur déclaré utilisateur empêche une classe d'être un agrégat. De cppreference , uniquement pour C ++ 20:

    Un agrégat est l'un des types suivants:

    • Type de tableau
    • Type de classe (généralement, structure ou syndicat), qui a
      • Pas de membres de données non statiques privés ou protégés
      • Aucun constructeur dénué ou héréditaire
      • Pas de classes de base virtuelles, privées ou protégées
      • Aucune fonction de membre virtuel

    À titre d'exemple, considérez:

    Swan s{.x=3};    // OK
    Cow c{.x=4};     // Error: Cow is not an aggregate
    

    output

    0010
    

    les deux premiers, Chien et Horse , ressemble à vos deux versions de dog . Ce ne sont pas des agrégats, car ils ont des membres privés. swan est un agrégat, mais Cow ne l'est pas, car il a un constructeur déclaré utilisateur.

    quelque chose qui fonctionne avec les agrégats, mais pas avec des non-non agrégats, est désigné d'initialiseurs (même page cppreference):

    #include <iostream>
    #include <type_traits>
    
    class Dog {
        int x;
    public:
        Dog()=default;
    };
    
    class Horse {
        int x;
    };
    
    class Swan {
    public: 
        int x;
    };
    
    class Cow {
    public:
        int x;
        Cow() = default;
    };
    
    int main() {
        std::cout << std::is_aggregate_v<Dog>;
        std::cout << std::is_aggregate_v<Horse>;
        std::cout << std::is_aggregate_v<Swan>;
        std::cout << std::is_aggregate_v<Cow>;
    }
    

    tl; dr: Je ne suis pas au courant d'une différence entre vos deux chien s, mais en général, la présence d'un constructeur déclaré utilisateur peut faire une différence.


    5 commentaires

    Constructeur par défaut par défaut et les gens disent que C ++ est une langue déroutante


    Y a-t-il une raison pour laquelle (oh mon - un pourquoi!) Les auteurs standard ont choisi de refuser les classes avec un état d'agrégat de constructeurs par défaut par défaut? Après tout, cela ne fait que faire quelque chose qui était explicite qui était là.


    @ Peter-Reinstatemonica Comme je l'ai compris, cela a quelque chose à voir avec la compatibilité en arrière avec C ++ 11, ou plutôt éviter les surprises lorsque le même code se comporte différemment. Je ne trouve plus la réponse qui l'a mentionné. Je vais devoir faire des recherches avant de pouvoir ajouter une explication à cette réponse


    @Silviomayolo faisant la distinction entre le constructeur par défaut spécifiquement par défaut et un constructeur par défaut par défaut par défaut. Tout ce dont vous avez besoin est une couche d'abstraction de plus;)


    @ Peter-ReinstateMonica: Un constructeur par défaut peut être par défaut en dehors de la déclaration de classe, dans une seule unité de compilation et invisible à tous les autres consommateurs de la classe. C ++ a donc dû choisir entre (a) par défaut à l'intérieur et à l'extérieur que la classe est la même et rendre la classe non agggat ou (b) par défaut à l'intérieur de la classe est la même que celle générée par le compilateur et en dehors de la classe ne fait que l'agrégation.



    15
    votes

    Je limiterai la portée à un constructeur par défaut, comme dans le code et la balise de la question. Pour la plupart, vous obtiendrez le même effet puisque = par défaut; signifie de manière lâche "Donnez-moi le compilateur généré par le compilateur". Il est important de noter ce qui est exactement > fait :

    s'il y a Aucun constructeur défini par l'utilisateur pour la classe X, un constructeur non explicite n'ayant aucun paramètre est implicitement déclaré comme par défaut. Un constructeur par défaut implicitement déclaré est un membre public en ligne de sa classe.

    Si votre déclaration modifie l'une d'entre elles, elle ne sera plus exactement la même que celle implicite. Dans la norme, dog () = default; est un constructeur déténué par l'utilisateur , mais pas un constructeur fourni par l'utilisateur. Il y a quelques petites différences entre le fait d'avoir un constructeur par défaut déténué par l'utilisateur et de ne pas avoir de constructeur.

    Comme mentionné, les agrégats ont été fixes :

    class c {
        int c; // Okay
    };
    
    class c2 {
        c2() = default;
        int c2; // Error
    };
    

    Avant le correctif, une telle classe a pu être créée via l'initialisation agrégée: not_agg {} code >. Naturellement, cela s'étend également à = par défaut; . Par [dcl.init.aggr] :

    Un agrégat est un tableau ou une classe avec

    • Aucun constructeur dénué ou héréditaire

    Il est également justifié de ce changement de l'annexe C:

    Supprimez l'initialisation d'agrégats susceptibles d'erreur qui peut s'appliquer nonobstant le constructeurs déclarés d'une classe.


    Une différence intéressante, mais très petite, c'est qu'une classe avec un constructeur déséqué par l'utilisateur est Non autorisé pour avoir des membres de données non statiques du même nom que la classe:

    struct not_agg {
        not_agg() = delete;
        int x;
    };
    

    Cela est dû à [class.mem] / 21 :

    p>

    En outre, si la classe T a un constructeur dénué par l'utilisateur, chaque membre de données non statique de la classe T doit avoir un nom différent de t.


    2 commentaires

    c - bon choix de nom, en considérant l'origine de cette règle.


    @ Storyteller-unslandermonica, oh mon cher, c'était une heureuse coïncidence.