Las respuestas de Angew y jaggedSpire son excelentes y se aplican ac ++ 11. Yc ++ 14. Yc ++ 17.
Sin embargo, en c ++ 20, las cosas cambian un poco y el ejemplo en el OP ya no se compilará:
class C {
C() = default;
};
C p;
auto q = C();
C r{};
auto s = C{};
Como se señala en las dos respuestas, la razón por la que las dos últimas declaraciones funcionan es porque C
es un agregado y esto es una inicialización agregada. Sin embargo, como resultado de P1008 (usando un ejemplo motivador no muy diferente del OP), la definición de agregado cambia en C ++ 20 a, desde [dcl.init.aggr] / 1 :
Un agregado es una matriz o una clase ([clase]) con
- sin constructores heredados o declarados por el usuario ([class.ctor]),
- sin miembros de datos no estáticos directos privados o protegidos ([class.access]),
- sin funciones virtuales ([class.virtual]), y
- sin clases base virtuales, privadas o protegidas ([class.mi]).
Énfasis mío. Ahora el requisito no es ningún constructor declarado por el usuario , mientras que solía ser (como ambos usuarios citan en sus respuestas y se puede ver históricamente para C ++ 11 , C ++ 14 y C ++ 17 ) sin constructores proporcionados por el usuario . El constructor predeterminado para C
es declarado por el usuario, pero no proporcionado por el usuario, y por lo tanto deja de ser un agregado en C ++ 20.
Aquí hay otro ejemplo ilustrativo de cambios agregados:
class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};
B
no era un agregado en C ++ 11 o C ++ 14 porque tiene una clase base. Como resultado, B{}
simplemente invoca el constructor predeterminado (declarado por el usuario pero no proporcionado por el usuario), que tiene acceso al A
constructor predeterminado protegido.
En C ++ 17, como resultado de P0017 , los agregados se ampliaron para permitir clases base. B
es un agregado en C ++ 17, lo que significa que B{}
es una inicialización agregada que tiene que inicializar todos los subobjetos, incluido el A
subobjeto. Pero debido a que A
el constructor predeterminado está protegido, no tenemos acceso a él, por lo que esta inicialización está mal formada.
En C ++ 20, debido al B
constructor declarado por el usuario, vuelve a dejar de ser un agregado, por lo que B{}
vuelve a invocar el constructor predeterminado y esta es una inicialización bien formada.
C c{};
la inicialización agregada, por lo que no se llama a ningún constructor?