95 votes

Quand un constructeur privé n'est-il pas un constructeur privé?

Disons que j'ai un type et que je veux rendre son constructeur par défaut privé. J'écris ce qui suit:

 class C {
    C() = default;
};

int main() {
    C c;           // error: C::C() is private within this context (g++)
                   // error: calling a private constructor of class 'C' (clang++)
                   // error C2248: 'C::C' cannot access private member declared in class 'C' (MSVC)
    auto c2 = C(); // error: as above
}
 

Génial.

Mais ensuite, le constructeur s'avère ne pas être aussi privé que je le pensais:

 class C {
    C() = default;
};

int main() {
    C c{};         // OK on all compilers
    auto c2 = C{}; // OK on all compilers
}    
 

Cela me semble être un comportement très surprenant, inattendu et explicitement indésirable. Pourquoi est-ce OK?

63voto

Angew Points 53063

Le truc, c'est C++14 8.4.2/5 [dcl.fct.def.par défaut]:

... Une fonction est fourni par l'utilisateur si elle est déclarée et non explicitement par défaut ou supprimé sur sa première déclaration. ...

Ce qui signifie qu' Cs'constructeur par défaut est en fait pas fourni par l'utilisateur, parce qu'il est explicitement fait défaut sur sa première déclaration. En tant que tel, C n'a pas fourni par l'utilisateur constructeurs et est donc un agrégat par 8.5.1/1 [dcl.init.aggr]:

Un agrégat est un tableau ou une classe (Clause 9) sans que l'utilisateur fourni par les constructeurs (12.1), aucune privé ou protégé non-membres de données statiques (Clause 11), pas de classes de base (article 10), et pas de fonctions virtuelles (10.3).

56voto

jaggedSpire Points 3509

Vous n'êtes pas d'appeler le constructeur par défaut, vous êtes à l'aide d'agrégation d'initialisation sur un type de regroupement. Les types d'agrégats sont autorisés à avoir un constructeur par défaut, tant que c'est en défaut, où il est d'abord déclaré:

À partir de [dcl.init.aggr]/1:

Un agrégat est un tableau ou une classe (Clause [classe]), avec

  • aucun utilisateur fourni par les constructeurs ([classe.ctor]) (y compris celles héritées ([espace de noms.udecl]) à partir d'une classe de base),
  • aucune privés ou protégés non-membres de données statiques (Clause [classe.accès]),
  • pas de fonctions virtuelles ([classe.virtuel]), et
  • pas de virtuel, privé ou protégé des classes de base ([classe.mi]).

et à partir de [dcl.fct.def.par défaut]/5

Explicitement-par défaut les fonctions et implicitement-les fonctions déclarées sont collectivement appelés par défaut des fonctions, et la mise en œuvre doit fournir implicite de définitions ([classe.ctor] [classe.dtor], [classe.copie]), ce qui pourrait signifier les définir comme étant supprimés. Une fonction est fourni par l'utilisateur si elle est déclarée et non explicitement par défaut ou supprimé sur sa première déclaration. L'utilisateur fournies explicitement par défaut de la fonction (c'est à dire, explicitement fait défaut après sa première déclaration) est défini au point où il est explicitement fait défaut; si une telle fonction est implicitement défini comme étant supprimée, le programme est mal formé. [ Note: la déclaration d'une fonction par défaut après sa première déclaration peut fournir une exécution efficace et concise, tout en permettant une stabilité interface binaire à une évolution de la base de code. - la note de fin ]

Ainsi, nos exigences pour un total de:

  • pas de non-publics des membres
  • pas de fonctions virtuelles
  • pas de virtuel ou non-public des classes de base
  • aucun utilisateur fourni par les constructeurs hérités ou autrement, qui ne permet que des constructeurs qui sont:
    • déclarée implicitement, ou
    • explicitement déclarée et définie en tant que par défaut en même temps.

C répond à toutes ces exigences.

Naturellement, vous pouvez peut-être débarrasser de ce faux par défaut de la construction de comportement en fournissant simplement un vide de constructeur par défaut, ou en définissant le constructeur par défaut après avoir déclaré:

class C {
    C(){}
};
// --or--
class C {
    C();
};
inline C::C() = default;

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