Mam strukturę z wieloma członkami tego samego typu, jak ten
struct VariablePointers {
VariablePtr active;
VariablePtr wasactive;
VariablePtr filename;
};
Problem polega na tym, że jeśli zapomnę zainicjować jednego z elementów struktury (np. wasactive
), To tak:
VariablePointers{activePtr, filename}
Kompilator nie będzie na to narzekał, ale będę miał częściowo zainicjowany obiekt. Jak mogę zapobiec tego rodzaju błędom? Mógłbym dodać konstruktor, ale skopiowałby listę zmiennych dwukrotnie, więc muszę trzykrotnie wpisać to wszystko!
Dodaj również odpowiedzi C ++ 11 , jeśli istnieje rozwiązanie dla C ++ 11 (obecnie jestem ograniczony do tej wersji). Mile widziane są jednak nowsze standardy językowe!
c++
aggregate-initialization
Johannes Schaub - litb
źródło
źródło
-Wmissing-field-initializers
flaga kompilacji.Odpowiedzi:
Oto sztuczka, która wyzwala błąd linkera, jeśli brakuje wymaganego inicjalizatora:
Stosowanie:
Wynik:
Ostrzeżenia:
Foo
w ogóle nie jest to agregat.źródło
Foo
zostanie zadeklarowany, nawet jeśli tak naprawdę nigdy nie zadzwonisz do operatora.W przypadku clang i gcc można skompilować,
-Werror=missing-field-initializers
co powoduje, że ostrzeżenie o brakujących inicjatorach pól zmienia się w błąd. godboltEdycja: W przypadku MSVC wydaje się, że nie jest emitowane żadne ostrzeżenie nawet na poziomie
/Wall
, więc nie sądzę, że możliwe jest ostrzeżenie o brakujących inicjalizatorach za pomocą tego kompilatora. godboltźródło
Chyba nie jest to eleganckie i poręczne rozwiązanie ... ale powinno również działać z C ++ 11 i dawać błąd czasu kompilacji (nie łącza).
Chodzi o to, aby dodać do struktury dodatkowy element członkowski, na ostatniej pozycji, typu bez domyślnej inicjalizacji (i który nie może zainicjować wartością typu
VariablePtr
(lub jakikolwiek typ poprzednich wartości)Przez przykład
W ten sposób jesteś zmuszony dodać wszystkie elementy do swojej zagregowanej listy inicjalizacyjnej, w tym wartość do jawnego zainicjowania ostatniej wartości (liczba całkowita
sentinel
, w przykładzie) lub otrzymujesz błąd „wywołanie usuniętego konstruktora błędu”.Więc
skompiluj i
nie.
Niestety także
nie kompiluje się.
-- EDYTOWAĆ --
Jak wskazali MSalters (dzięki), w moim oryginalnym przykładzie występuje wada (kolejna wada):
bar
wartość może być zainicjowanachar
wartością (którą można przekształcićint
), więc działa następująca inicjalizacjai może to być bardzo mylące.
Aby uniknąć tego problemu, dodałem następujący konstruktor usuniętych szablonów
więc poprzednia
f4
deklaracja podaje błąd kompilacji, ponieważd
wartość jest przechwytywana przez konstruktor szablonu, który jest usuwanyźródło
foo f;
nie można go skompilować, ale może to bardziej cecha niż wada tej sztuczki. Akceptuje, jeśli nie ma lepszej propozycji niż ta.enum
i nazwaćinit_list_end
(po prostulist_end
) jego wartośćenum
; ale czytelność dodaje dużo pisania na maszynie, więc biorąc pod uwagę, że dodatkowa wartość jest słabym punktem tej odpowiedzi, nie wiem, czy to dobry pomysł.constexpr static int eol = 0;
w nagłówkubar
.test{a, b, c, eol}
wydaje mi się dość czytelny.bar::eol
; jest prawie tak samo jakenum
wartość; ale nie sądzę, żeby to było ważne: rdzeń odpowiedzi brzmi: „dodaj do swojej struktury dodatkowego członka, na ostatniej pozycji, typu bez domyślnej inicjalizacji”;bar
część jest po prostu banalny przykład, aby pokazać, że rozwiązanie działa; dokładny „typ bez domyślnej inicjalizacji” powinien zależeć od okoliczności (IMHO).W przypadku CppCoreCheck istnieje reguła sprawdzająca dokładnie, czy wszystkie elementy zostały zainicjowane i które można przekształcić z ostrzeżenia w błąd - zwykle dotyczy to całego programu.
Aktualizacja:
Reguła, którą chcesz sprawdzić, jest częścią bezpieczeństwa typów
Type.6
:źródło
Najprostszym sposobem jest nie nadanie typowi elementów konstruktora bez argumentu:
Inna opcja: jeśli Twoi członkowie są const &, musisz zainicjować ich wszystkich:
Jeśli możesz żyć z jednym manekinem const i członkiem, możesz połączyć to z pomysłem @ max66 na wartownika.
Od cppreference https://en.cppreference.com/w/cpp/language/aggregate_initialization
Inną opcją jest wzięcie pomysłu wartownika max66 i dodanie cukru syntaktycznego dla czytelności
źródło
A
nieporuszalny i zmienia semantykę kopiowania (A
nie jest już zbiorem wartości, że tak powiem) :(1,2,3
obiekty są faktycznie lokalnymi obiektami w automatycznym magazynie, które wykraczają poza zakres po zakończeniu funkcji. I sprawia, że sizeof (A) 24 zamiast 3 w systemie z 64-bitowymi wskaźnikami (jak x86-64).