Ich habe eine Struktur mit vielen Mitgliedern des gleichen Typs, wie diese
struct VariablePointers {
VariablePtr active;
VariablePtr wasactive;
VariablePtr filename;
};
Das Problem ist, dass, wenn ich vergesse, eines der Strukturelemente (z. B. wasactive
) wie folgt zu initialisieren :
VariablePointers{activePtr, filename}
Der Compiler wird sich nicht darüber beschweren, aber ich werde ein Objekt haben, das teilweise initialisiert ist. Wie kann ich diese Art von Fehler verhindern? Ich könnte einen Konstruktor hinzufügen, aber er würde die Liste der Variablen zweimal duplizieren, also muss ich das alles dreimal eingeben!
Bitte fügen Sie auch C ++ 11- Antworten hinzu, wenn es eine Lösung für C ++ 11 gibt (derzeit bin ich auf diese Version beschränkt). Neuere Sprachstandards sind jedoch ebenfalls willkommen!
c++
aggregate-initialization
Johannes Schaub - litb
quelle
quelle
-Wmissing-field-initializers
Kompilierungsflag.Antworten:
Hier ist ein Trick, der einen Linkerfehler auslöst, wenn ein erforderlicher Initialisierer fehlt:
Verwendungszweck:
Ergebnis:
Vorsichtsmaßnahmen:
Foo
überhaupt um ein Aggregat handelt.quelle
Foo
es deklariert ist, auch wenn Sie den Operator nie wirklich anrufen.Für clang und gcc können Sie kompilieren
-Werror=missing-field-initializers
, wodurch die Warnung bei fehlenden Feldinitialisierern in einen Fehler umgewandelt wird. GodboltBearbeiten: Für MSVC scheint selbst auf Ebene keine Warnung ausgegeben zu werden
/Wall
, daher glaube ich nicht, dass es möglich ist, mit diesem Compiler vor fehlenden Initialisierern zu warnen. Godboltquelle
Keine elegante und handliche Lösung, nehme ich an ... sollte aber auch mit C ++ 11 funktionieren und einen Fehler bei der Kompilierung (nicht bei der Verbindungszeit) verursachen.
Die Idee ist, in Ihrer Struktur an der letzten Position ein zusätzliches Element eines Typs ohne Standardinitialisierung hinzuzufügen (und das nicht mit einem Wert vom Typ initialisiert werden kann
VariablePtr
(oder was auch immer der Typ der vorhergehenden Werte ist).Zum Beispiel
Auf diese Weise müssen Sie alle Elemente zu Ihrer aggregierten Initialisierungsliste hinzufügen, den Wert zum expliziten Initialisieren des letzten Werts einschließen (
sentinel
im Beispiel eine Ganzzahl für ), oder Sie erhalten den Fehler "Aufruf an gelöschten Konstruktor des Balkens".Damit
kompilieren und
nicht.
Leider auch
kompiliert nicht.
- BEARBEITEN -
Wie von MSalters (danke) angegeben, gibt es in meinem ursprünglichen Beispiel einen Fehler (einen weiteren Fehler): Ein
bar
Wert könnte mit einemchar
Wert initialisiert werden (der in konvertierbar istint
), daher funktioniert die folgende Initialisierungund das kann sehr verwirrend sein.
Um dieses Problem zu vermeiden, habe ich den folgenden gelöschten Vorlagenkonstruktor hinzugefügt
Daher
f4
gibt die vorhergehende Deklaration einen Kompilierungsfehler aus, da derd
Wert vom gelöschten Vorlagenkonstruktor abgefangen wirdquelle
foo f;
nicht kompiliert werden kann, aber vielleicht ist das eher eine Funktion als ein Fehler bei diesem Trick. Wird akzeptieren, wenn es keinen besseren Vorschlag als diesen gibt.enum
undinit_list_end
(o einfachlist_end
) einen Wert davon benennenenum
; Aber die Lesbarkeit fügt viel Schreibmaschine hinzu. Da der zusätzliche Wert der Schwachpunkt dieser Antwort ist, weiß ich nicht, ob es eine gute Idee ist.constexpr static int eol = 0;
in der Kopfzeile von hinzufügenbar
.test{a, b, c, eol}
scheint mir ziemlich lesbar.bar::eol
; es ist fast so, als würde man einenenum
Wert übergeben; aber ich denke nicht, dass es wichtig ist: Der Kern der Antwort ist "füge in deiner Struktur ein zusätzliches Mitglied an der letzten Position eines Typs ohne Standardinitialisierung hinzu"; Derbar
Teil ist nur ein triviales Beispiel, um zu zeigen, dass die Lösung funktioniert. Der genaue "Typ ohne Standardinitialisierung" sollte von den Umständen abhängen (IMHO).Für CppCoreCheck gibt es eine Regel, mit der genau überprüft werden kann, ob alle Mitglieder initialisiert wurden und die von einer Warnung in einen Fehler umgewandelt werden kann - das ist normalerweise normalerweise programmweit.
Aktualisieren:
Die Regel, die Sie überprüfen möchten, ist Teil der Typensicherheit
Type.6
:quelle
Der einfachste Weg ist, dem Typ der Mitglieder keinen Konstruktor ohne Argumente zu geben:
Eine weitere Option: Wenn Ihre Mitglieder const & sind, müssen Sie alle initialisieren:
Wenn Sie mit einem Dummy const & member leben können, können Sie dies mit der Idee von @ max66 eines Sentinels kombinieren.
Von cppreference https://en.cppreference.com/w/cpp/language/aggregate_initialization
Eine andere Möglichkeit besteht darin, die Sentinel-Idee von max66 zu übernehmen und zur besseren Lesbarkeit etwas syntaktischen Zucker hinzuzufügen
quelle
A
unbeweglich und verändert die Kopiersemantik (A
ist sozusagen kein Aggregat von Werten mehr) :(1,2,3
Objekte sind effektiv lokale Objekte im automatischen Speicher, die nach Beendigung der Funktion den Gültigkeitsbereich verlassen. Auf einem System mit 64-Bit-Zeigern (wie x86-64) wird die Größe von (A) 24 anstelle von 3 festgelegt.