¿Cuándo un constructor privado no es un constructor privado?

Digamos que tengo un tipo y quiero hacer que su constructor predeterminado sea privado. Yo escribo lo siguiente:

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 } 

Estupendo.

Pero luego, el constructor resulta no ser tan privado como pensé que era:

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

Esto me parece una conducta muy sorprendente, inesperada y explícitamente indeseable. ¿Por qué está bien?

El truco está en C ++ 14 8.4.2 / 5 [dcl.fct.def.default]:

… Una función es proporcionada por el usuario si ha sido declarada por el usuario y no está explícitamente predeterminada o eliminada en su primera statement. …

Lo que significa que el constructor predeterminado de C no está proporcionado por el usuario, ya que fue explícitamente predeterminado en su primera statement. Como tal, C no tiene constructores proporcionados por el usuario y, por lo tanto, es un agregado por 8.5.1 / 1 [dcl.init.aggr]:

Un agregado es una matriz o una clase (Cláusula 9) sin constructores proporcionados por el usuario (12.1), sin miembros de datos no estáticos protegidos o privados (Cláusula 11), sin clases base (Cláusula 10) y sin funciones virtuales (10.3 )

No está llamando al constructor predeterminado, está usando la inicialización agregada en un tipo agregado. Los tipos de agregado pueden tener un constructor predeterminado, siempre que se establezca por defecto donde se haya declarado por primera vez:

Desde [dcl.init.aggr] / 1 :

Un agregado es una matriz o una clase (Cláusula [clase]) con

  • ningún constructor proporcionado por el usuario ([class.ctor]) (incluidos los heredados ([namespace.udecl]) de una clase base),
  • ningún miembro de datos no estáticos privados o protegidos (Cláusula [class.access]),
  • sin funciones virtuales ([class.virtual]), y
  • no hay clases base virtuales, privadas o protegidas ([class.mi]).

y desde [dcl.fct.def.default] / 5

Las funciones explícitamente predeterminadas y las funciones declaradas implícitamente se denominan colectivamente funciones predeterminadas, y la implementación proporcionará definiciones implícitas para ellas ([class.ctor] [class.dtor], [class.copy]), lo que podría significar que se definan como eliminadas. . Una función es proporcionada por el usuario si es declarada por el usuario y no está explícitamente predeterminada o eliminada en su primera statement. Una función predeterminada explícitamente proporcionada por el usuario (es decir, explícitamente predeterminada después de su primera statement) se define en el punto donde está explícitamente predeterminada; si tal función se define implícitamente como eliminada, el progtwig está mal formado. [Nota: declarar una función como predeterminada después de su primera statement puede proporcionar una ejecución eficiente y una definición concisa al tiempo que permite una interfaz binaria estable a una base de código en evolución. – nota final]

Por lo tanto, nuestros requisitos para un agregado son:

  • no miembros no públicos
  • sin funciones virtuales
  • no hay clases base virtuales o no públicas
  • no hay constructores proporcionados por el usuario heredados o no, lo que permite solo constructores que son:
    • implícitamente declarado, o
    • explícitamente declarado y definido como predeterminado al mismo tiempo.

C cumple todos estos requisitos.

Naturalmente, puede deshacerse de este falso comportamiento de construcción predeterminado simplemente proporcionando un constructor predeterminado vacío, o definiendo el constructor como predeterminado después de declararlo:

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