Les réponses d' Angew et de JaggedSpire sont excellentes et s'appliquent àc ++ 11. Etc ++ 14. Etc ++ 17.
Cependant, dans c ++ 20, les choses changent un peu et l'exemple dans l'OP ne sera plus compilé:
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
Comme indiqué par les deux réponses, la raison pour laquelle les deux dernières déclarations fonctionnent est parce qu'il C
s'agit d'un agrégat et qu'il s'agit d'une initialisation d'agrégat. Cependant, à la suite de P1008 (en utilisant un exemple motivant pas trop différent de l'OP), la définition de l'agrégat change dans C ++ 20 à, de [dcl.init.aggr] / 1 :
Un agrégat est un tableau ou une classe ([class]) avec
- aucun constructeur déclaré ou hérité par l'utilisateur ([class.ctor]),
- aucun membre de données non statique direct privé ou protégé ([class.access]),
- pas de fonctions virtuelles ([class.virtual]), et
- pas de classes de base virtuelles, privées ou protégées ([class.mi]).
Soulignez le mien. Désormais, il ne s'agit pas de constructeurs déclarés par l'utilisateur , alors qu'auparavant (comme les deux utilisateurs le citent dans leurs réponses et peuvent être consultés historiquement pour C ++ 11 , C ++ 14 et C ++ 17 ), aucun constructeur fourni par l'utilisateur . Le constructeur par défaut pour C
est déclaré par l'utilisateur, mais pas fourni par l'utilisateur, et cesse donc d'être un agrégat dans C ++ 20.
Voici un autre exemple illustratif de modifications globales:
class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};
B
n'était pas un agrégat en C ++ 11 ou C ++ 14 car il a une classe de base. En conséquence, B{}
appelle simplement le constructeur par défaut (déclaré par l'utilisateur mais pas fourni par l'utilisateur), qui a accès au A
constructeur par défaut protégé de.
Dans C ++ 17, à la suite de P0017 , les agrégats ont été étendus pour permettre des classes de base. B
est un agrégat en C ++ 17, ce qui signifie qu'il B{}
s'agit d'une initialisation d'agrégat qui doit initialiser tous les sous-objets, y compris le A
sous - objet. Mais comme A
le constructeur par défaut est protégé, nous n'y avons pas accès, donc cette initialisation est mal formée.
En C ++ 20, à cause du B
constructeur déclaré par l'utilisateur, il cesse à nouveau d'être un agrégat, donc B{}
revient à appeler le constructeur par défaut et c'est à nouveau une initialisation bien formée.
C c{};
initialisation de l'agrégat n'est-elle pas donc aucun constructeur n'est appelé?