Quand un constructeur privé 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
}
Super.
mais alors, 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?
2 réponses
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. ...
qui signifie que le constructeur par défaut de C
est en fait pas fourni par l'utilisateur, parce qu'il a été explicitement par défaut sur sa première déclaration. En tant que tel, C
n'a pas de constructeur fourni par l'utilisateur et est donc un agrégat selon 8.5.1/1 [dcl.initialisation.aggr]:
Un globale est un tableau ou une classe (Clause 9) sans que l'utilisateur fourni par les constructeurs (12.1), aucune privé ou membres de données non statiques protégés (Clause 11), Pas de classes de base (Clause 10), et pas de fonctions virtuelles (10.3).
vous n'appelez pas le constructeur par défaut, vous utilisez l'initialisation agrégée sur un type agrégé. Les types d'agrégats sont autorisés à avoir un constructeur en défaut, tant qu'il est en défaut là où il a été déclaré pour la première fois:
de [dcl.initialisation.aggr] / 1 :
un agrégat est un réseau ou une classe (Clause [class]) avec
- aucun constructeur fourni par l'utilisateur ([classe.ctor]) (y compris ceux hérités ([namespace.udecl]) à partir d'une classe de base),
- non privé ou protégé non-membres de données statiques (Clause [classe.accès]),
- pas de fonctions virtuelles ([classe.virtual]), et
- aucune classe de base virtuelle, privée ou protégée ([class.mi.)]
et de [dcl.fct.def.par défaut]/5
explicitement-les fonctions par défaut et les fonctions implicitement-déclarées sont collectivement appelées fonctions par défaut, et la mise en œuvre doit fournir des définitions implicites pour elles ([class.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. une fonction fournie explicitement par l'utilisateur et par défaut (c.-à-d. explicitement par défaut) après sa première déclaration) est défini au point où il est explicitement par défaut; si une telle fonction est implicitement définie comme supprimée, le programme est mal formé. [Note: déclarer une fonction comme par défaut après sa première déclaration peut fournir une exécution efficace et une définition concise tout en permettant une interface binaire stable à une base de code en évolution. - note finale]
ainsi, nos exigences pour un agrégat sont:
- non-public "membres de 1519130920"
- pas de fonctions virtuelles
- non virtuel ou non-public des classes de base
- aucun constructeur fourni par l'utilisateur, hérité ou non, qui ne permet que les constructeurs qui sont:
- implicitement déclaré, ou
- explicitement déclaré et défini comme étant en défaut au même moment.
C
remplit toutes ces conditions.
naturellement, vous pouvez être débarrassé de ce comportement de construction par défaut en fournissant simplement un constructeur par défaut vide, ou en définissant le constructeur par défaut après l'avoir déclaré:
class C {
C(){}
};
// --or--
class C {
C();
};
inline C::C() = default;