De Angew e jaggedSpire's ' respostas de são excelentes e se aplicam ac ++ 11. Ec ++ 14. Ec ++ 17.
No entanto, em c ++ 20, as coisas mudam um pouco e o exemplo no OP não compilará mais:
class C {
C() = default;
};
C p; // always error
auto q = C(); // always error
C r{}; // ok on C++11 thru C++17, error on C++20
auto s = C{}; // ok on C++11 thru C++17, error on C++20
Conforme apontado pelas duas respostas, o motivo pelo qual as duas últimas declarações funcionam é porque C
é um agregado e esta é uma inicialização de agregado. No entanto, como resultado de P1008 (usando um exemplo motivador não muito diferente do OP), a definição de alterações agregadas em C ++ 20 para, de [dcl.init.aggr] / 1 :
Um agregado é uma matriz ou uma classe ([classe]) com
- nenhum construtor declarado pelo usuário ou herdado ([class.ctor]),
- sem membros de dados não estáticos diretos privados ou protegidos ([class.access]),
- sem funções virtuais ([class.virtual]), e
- nenhuma classe base virtual, privada ou protegida ([class.mi]).
Ênfase minha. Agora, o requisito não é nenhum construtor declarado pelo usuário , ao passo que costumava ser (como ambos os usuários citam em suas respostas e pode ser visto historicamente para C ++ 11 , C ++ 14 e C ++ 17 ) nenhum construtor fornecido pelo usuário . O construtor padrão para C
é declarado pelo usuário, mas não fornecido pelo usuário e, portanto, deixa de ser um agregado em C ++ 20.
Aqui está outro exemplo ilustrativo de alterações agregadas:
class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};
B
não era um agregado em C ++ 11 ou C ++ 14 porque tem uma classe base. Como um resultado,B{}
apenas invoca o construtor padrão (declarado pelo usuário, mas não fornecido pelo usuário), que tem acesso ao A
construtor padrão protegido de.
Em C ++ 17, como resultado de P0017 , os agregados foram estendidos para permitir classes básicas. B
é um agregado em C ++ 17, o que significa que B{}
é uma inicialização de agregação que deve inicializar todos os subobjetos - incluindo o A
subobjeto. Mas, como A
o construtor padrão de é protegido, não temos acesso a ele, então essa inicialização está malformada.
No C ++ 20, por causa do B
construtor declarado pelo usuário, ele novamente deixa de ser uma agregação, então B{}
volta a invocar o construtor padrão e esta é novamente uma inicialização bem formada.
C c{};
a inicialização de agregação, então nenhum construtor é chamado?