Pourquoi est-ce que C++, un utilisateur par défaut fourni par le constructeur pour défaut de construire un const objet?
La norme C++ (section 8.5) dit:
Si un programme d'appels pour l'initialisation par défaut d'un objet d'une const qualifiés de type T, T est un type de classe avec un utilisateur par défaut fourni par le constructeur.
Pourquoi? Je ne peux pas penser à une raison pourquoi un utilisateur fourni par le constructeur est requis dans ce cas.
struct B{
B():x(42){}
int doSomeStuff() const{return x;}
int x;
};
struct A{
A(){}//other than "because the standard says so", why is this line required?
B b;//not required for this example, just to illustrate
//how this situation isn't totally useless
};
int main(){
const A a;
}
- La ligne ne semble pas être nécessaire dans votre exemple (voir la section ideone.com/qqiXR) parce que vous avez déclaré, mais n'a pas de définition/initialiser
a
, mais gcc-4.3.4 l'accepte, même lorsque vous ne (voir la section ideone.com/uHvFS) - L'exemple ci-dessus les deux déclare et définit
a
. Comeau produit une erreur "variable const "un" exige un initialiseur de classe "A" n'a pas déclaré explicitement le constructeur par défaut" si la ligne est commentée. - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#253
- C'est corrigé dans C++11, vous pouvez écrire
const A a{}
🙂
Vous devez vous connecter pour publier un commentaire.
Cela a été considéré comme un défaut (à l'encontre de toutes les versions de la norme) et il a été résolu par De base Groupe de Travail (GTC) Défaut 253. La nouvelle formulation de la norme en http://eel.is/c++projet/dcl.init#7
Cette formulation signifie essentiellement que l'évident code fonctionne. Si vous initialisez l'ensemble de vos bases et les membres, vous pouvez dire
A const a;
peu importe comment ou si vous sort tout les constructeurs.gcc a accepté ce depuis 4.6.4. clang a accepté ce depuis 3.9.0. Visual Studio accepte également (au moins en 2017, ne sais pas si tôt).
struct A { int n; A() = default; }; const A a;
tout en permettantstruct B { int n; B() {} }; const B b;
parce que le nouveau libellé indique toujours "fourni par l'utilisateur" de ne pas "user-déclarée" et je me retrouve à me gratter la tête pourquoi le comité a choisi d'exclure explicitement-par défaut constructeurs par défaut de ce DR, les obligeant à faire nos classes non-trivial si nous voulons const objets non initialisée avec les membres.MyPOD
être un PODstruct
,static MyPOD x;
, qui s'appuie sur le zéro-initialisation (est-ce le bon?) pour définir la variable de membre(s) de manière appropriée -- compile, maisstatic const MyPOD x;
ne le fait pas. Est-il une chance que que sera corrigé?La raison en est que si la classe n'est pas définie par l'utilisateur et le constructeur, alors il peut être POD, et la GOUSSE de classe n'est pas initialisé par défaut. Donc, si vous déclarez une const objet de POD qui est non initialisée, à quoi ça sert? Je pense donc que la Norme impose cette règle afin que l'objet peut effectivement être utile.
Mais si vous faites de la classe non-POD:
Noter la différence entre la nacelle et de la non-POD.
Définis par l'utilisateur constructeur est une manière de faire de la classe non-POD. Il ya plusieurs façons que vous pouvez faire.
Avis nonPOD_B n'est pas défini par l'utilisateur défini constructeur. Compiler. Compiler:
Et commenter la fonction virtuelle, puis il donne une erreur, comme prévu:
Bien, je pense que vous avez mal compris le passage. Il est le premier à dire cela (§8.5/9):
Il parle de la non-POD classe éventuellement cv qualifiés type. Qui est, le non-POD objet doit être par défaut-initialisé, si il n'y a pas d'initialiseur spécifié. Et qu'est-ce que par défaut-initialisé? Pour les non-POD, la spec dit (§8.5/5),
Simplement, il parle de constructeur par défaut de T, si ses définies par l'utilisateur ou généré par le compilateur est pas pertinent.
Si vous êtes clair jusqu'à présent, puis de comprendre ce que la spécification suivante est dit ((§8.5/9),
Donc ce texte l'indique, le programme va être mal formé si l'objet est de const qualifiés type de POD, et il n'y a pas d'initialiseur spécifié (parce que les GOUSSES ne sont pas initialisé par défaut):
Par la voie, cette compile fine, en raison de sa non-POD, et peut être par défaut-initialisé.
nonPOD_B
n'ont pas fourni par l'utilisateur dans le constructeur par défaut de sorte que la ligneconst nonPOD_B b2
n'est pas autorisé.B
dans la question). Mais l'utilisateur par défaut fourni par le constructeur est toujours nécessaire dans ce cas.const
non-POD objet pour obtenir initialisé par l'appel par défaut-constructeur généré par le compilateur.p1.i = 10;
au niveau espace de noms auquel casp1
sera statiquement initialisé à zéro. Je suis au courant de cette affaire, et en ont discuté en détail ici : Ce qui est dynamique intialization de l'objet en c++?De la Pure spéculation de ma part, mais considérer que les autres types de restriction semblable, trop:
N'est donc pas seulement cette règle homogène, mais aussi (de manière récursive) empêche non
const
(sous -) objets:Comme pour l'autre côté de la question (en l'autorisant pour les types avec un constructeur par défaut), je pense que l'idée est qu'un type avec un utilisateur par défaut fourni par le constructeur est censé toujours être dans un certain état cohérent après la construction. Notez que les règles qu'ils sont de permettre la suivante:
Alors on pourrait peut-être formuler une règle comme "au moins un membre doit être intelligemment initialisé à un utilisateur par défaut fourni par le constructeur', mais c'est beaucoup trop de temps passé à essayer de se protéger contre Murphy. C++ a tendance à faire confiance au programmeur sur certains points.
A(){}
, l'erreur de s'en aller, afin de ne pas empêcher quoi que ce soit. La règle ne fonctionne pas de manière récursive -X(){}
n'est jamais nécessaire, pour cet exemple.const A a {};
(const A a = A();
pour C++03)? J'avais plus tôt le faire que d'ajouter un constructeur.const A a {};
(c'est la valeur d'initialisation).Félicitations, vous avez inventé un cas dans lequel il n'a pas besoin d'être définis par utilisateur constructeur de la
const
déclaration sans initialiseur de sens.Maintenant, pouvez-vous venir avec une re-formulation de la règle qui couvre votre cas, mais qui fait encore le cas qui devrait être illégal illégal? Est-il inférieur à 5 ou 6 paragraphes? Est-il facile et évident de savoir comment elle devrait être appliquée dans n'importe quelle situation?
Je postulons que venir avec une règle qui permet de la déclaration que vous avez créé pour donner un sens est vraiment dur, et faire en sorte que la règle peut être appliquée d'une manière qui fait sens pour les gens lors de la lecture de code est encore plus difficile. Je préfère un peu restrictive de la règle qui a été la bonne chose à faire dans la plupart des cas à une très nuancée et complexe de la règle qui a été difficile à comprendre et à appliquer.
La question est, est-il une raison impérieuse de la règle doit être plus complexe? Est-il un code qui serait autrement très difficile d'écrire ou de comprendre ce que peut être écrit beaucoup plus simplement, si la règle est plus complexe?
const POD x;
illégale tout commeconst int x;
est illégal (ce qui est logique, parce que c'est inutile pour un POD), mais assurez -const NonPOD x;
juridique (ce qui est logique, car il pourrait avoir des sous-objets contenant des constructeurs/destructeurs, ou avoir une durée de constructeur/destructeur de lui-même).struct NonPod { std::string s; }; const NonPod x;
Et donne une erreur lorsque NonPod eststruct NonPod { int i; std::string s; }; const NonPod x;
Je regardais Timur Doumler de parler à la Réunion C++ 2018 et j'ai enfin compris pourquoi la norme exige qu'un utilisateur fourni par le constructeur ici, et non pas simplement un utilisateur déclaré. Il a à voir avec les règles de valeur d'initialisation.
Considérons deux classes:
A
a un l'utilisateur déclaré constructeur,B
a un fourni par l'utilisateur constructeur:À première vue, vous pourriez penser que ces deux constructeurs se comporte de la même. Mais à voir la façon dont la valeur d'initialisation se comporte différemment, tout seul défaut initialisation se comporte de la même:
A a;
est par défaut d'initialisation: le membreint x
est non initialisée.B b;
est par défaut d'initialisation: le membreint x
est non initialisée.A a{};
est la valeur d'initialisation: le membreint x
est zéro-initialisé.B b{};
est la valeur d'initialisation: le membreint x
est non initialisée.Maintenant voir ce qui se passe quand on ajoute
const
:const A a;
est par défaut d'initialisation: c'est mal formé en raison de la règle citée dans la question.const B b;
est par défaut d'initialisation: le membreint x
est non initialisée.const A a{};
est la valeur d'initialisation: le membreint x
est zéro-initialisé.const B b{};
est la valeur d'initialisation: le membreint x
est non initialisée.Un non initialisée
const
scalaire (par exemple, laint x
membre) serait inutile: il est écrit mal formé (parce que c'estconst
) et la lecture de c'est UB (parce que c'est une valeur indéterminée). Donc, cette règle ne vous empêche de créer une telle chose, en vous forçant à soit ajouter un initialiser ou opt-in pour les comportements dangereux par l'ajout d'un utilisateur fourni par le constructeur.Je pense qu'il serait agréable d'avoir un attribut comme
[[uninitialized]]
pour indiquer au compilateur lorsque vous êtes intentionnellement pas de l'initialisation d'un objet. Nous ne serions pas obligés de faire de notre classe pas trivialement par défaut constructible à obtenir autour de ce coin de cas.