J'ai une structure avec de nombreux membres du même type, comme ceci
struct VariablePointers {
VariablePtr active;
VariablePtr wasactive;
VariablePtr filename;
};
Le problème est que si j'oublie d'initialiser l'un des membres de la structure (par exemple wasactive
), comme ceci:
VariablePointers{activePtr, filename}
Le compilateur ne s'en plaindra pas, mais j'aurai un objet partiellement initialisé. Comment éviter ce genre d'erreur? Je pourrais ajouter un constructeur, mais cela dupliquerait la liste des variables deux fois, donc je dois taper tout cela trois fois!
Veuillez également ajouter des réponses C ++ 11 , s'il existe une solution pour C ++ 11 (actuellement, je suis limité à cette version). Les normes linguistiques plus récentes sont également les bienvenues!
c++
aggregate-initialization
Johannes Schaub - litb
la source
la source
-Wmissing-field-initializers
drapeau de compilation.Réponses:
Voici une astuce qui déclenche une erreur de l'éditeur de liens si un initialiseur requis est manquant:
Usage:
Résultat:
Mises en garde:
Foo
du tout d'être un agrégat.la source
Foo
est déclaré, même si vous n'appelez jamais réellement l'opérateur.Pour clang et gcc, vous pouvez compiler avec
-Werror=missing-field-initializers
qui transforme l'avertissement sur les initialiseurs de champ manquants en erreur. GodboltEdit: Pour MSVC, il ne semble y avoir aucun avertissement émis même au niveau
/Wall
, donc je ne pense pas qu'il soit possible d'avertir des initialiseurs manquants avec ce compilateur. Godboltla source
Ce n'est pas une solution élégante et pratique, je suppose ... mais devrait également fonctionner avec C ++ 11 et donner une erreur de compilation (pas de liaison).
L'idée est d'ajouter dans votre structure un membre supplémentaire, en dernière position, d'un type sans initialisation par défaut (et qui ne peut pas s'initialiser avec une valeur de type
VariablePtr
(ou quel que soit le type des valeurs précédentes)Par exemple
De cette façon, vous êtes obligé d'ajouter tous les éléments dans votre liste d'initialisation agrégée, inclus la valeur pour initialiser explicitement la dernière valeur (un entier pour
sentinel
, dans l'exemple) ou vous obtenez un "appel au constructeur supprimé de 'bar'".Donc
compiler et
non.
Malheureusement aussi
ne compile pas.
-- ÉDITER --
Comme indiqué par MSalters (merci), il y a un défaut (un autre défaut) dans mon exemple d'origine: une
bar
valeur pourrait être initialisée avec unechar
valeur (qui est convertible enint
), donc fonctionne l'initialisation suivanteet cela peut être très déroutant.
Pour éviter ce problème, j'ai ajouté le constructeur de modèle supprimé suivant
de sorte que la
f4
déclaration précédente donne une erreur de compilation car lad
valeur est interceptée par le constructeur de modèle qui est suppriméla source
foo f;
compilation impossible, mais c'est peut-être plus une fonctionnalité qu'un défaut de cette astuce. Acceptera s'il n'y a pas de meilleure proposition que celle-ci.enum
, et nommerinit_list_end
(o simplementlist_end
) une valeur de celaenum
; mais la lisibilité ajoute beaucoup de dactylographie, donc, étant donné que la valeur supplémentaire est le point faible de cette réponse, je ne sais pas si c'est une bonne idée.constexpr static int eol = 0;
dans l'en-tête debar
.test{a, b, c, eol}
me semble assez lisible.bar::eol
; c'est presque comme passer uneenum
valeur; mais je ne pense pas que ce soit important: le cœur de la réponse est "ajoutez dans votre structure un membre supplémentaire, en dernière position, d'un type sans initialisation par défaut"; labar
partie est juste un exemple trivial pour montrer que la solution fonctionne; le "type sans initialisation par défaut" exact devrait dépendre des circonstances (à mon humble avis).Pour CppCoreCheck, il existe une règle pour vérifier exactement cela, si tous les membres ont été initialisés et qui peuvent être transformés d'avertissement en erreur - cela est bien sûr à l'échelle du programme.
Mise à jour:
La règle que vous souhaitez vérifier fait partie de la sécurité des types
Type.6
:la source
Le moyen le plus simple est de ne pas donner au type des membres un constructeur sans argument:
Autre option: si vos membres sont const &, vous devez tous les initialiser:
Si vous pouvez vivre avec un membre et un constable factice, vous pouvez combiner cela avec l'idée de @ max66 d'une sentinelle.
Depuis cppreference https://en.cppreference.com/w/cpp/language/aggregate_initialization
Une autre option consiste à prendre l'idée sentinelle de max66 et à ajouter du sucre syntaxique pour plus de lisibilité
la source
A
inamovible et change la copie sémantique (A
n'est plus un agrégat de valeurs, pour ainsi dire) :(1,2,3
objets sont en fait des locaux dans le stockage automatique qui sortent du cadre lorsque la fonction se termine. Et cela fait la taille de (A) 24 au lieu de 3 sur un système avec des pointeurs 64 bits (comme x86-64).