Obecnie czytam kod źródłowy Protocol Buffer
i znalazłem jeden dziwne enum
kody zdefiniowane tutaj
~scoped_ptr() {
enum { type_must_be_complete = sizeof(C) };
delete ptr_;
}
void reset(C* p = NULL) {
if (p != ptr_) {
enum { type_must_be_complete = sizeof(C) };
delete ptr_;
ptr_ = p;
}
}
Dlaczego enum { type_must_be_complete = sizeof(C) };
tutaj zdefiniowano? do czego jest to używane?
ptr_
siebie wsizeof
assizeof(*ptr_)
zamiastsizeof(C)
.Odpowiedzi:
Ta sztuczka pozwala uniknąć UB, zapewniając, że definicja C jest dostępna podczas kompilacji tego destruktora. W przeciwnym razie kompilacja zakończy się niepowodzeniem, ponieważ
sizeof
nie można określić niekompletnego typu (zadeklarowane typy do przodu), ale można użyć wskaźników.W skompilowanym pliku binarnym ten kod zostałby zoptymalizowany i nie miałby żadnego efektu.
Zauważ, że: Usunięcie niekompletnego typu może być niezdefiniowanym zachowaniem z 5.3.5 / 5 :.
g++
wydaje nawet następujące ostrzeżenie:źródło
sizeof(C)
zakończy się niepowodzeniem w czasie kompilacji, jeśliC
nie jest kompletnym typem. Ustawienie zakresu lokalnegoenum
na to sprawia, że instrukcja jest niegroźna w czasie wykonywania.Jest to sposób programisty chroniącego się przed samym sobą: zachowanie kolejnego
delete ptr_
na niekompletnym typie jest niezdefiniowane, jeśli ma nietrywialny destruktor.źródło
delete
? A jeśli tak, to dlaczego i tak kompilator tego nie łapie?C = void
? GdybyC
był tylko niezdefiniowanym typem, czydelete
instrukcja już nie zawiodłaby?Aby zrozumieć
enum
, zacznij od rozważenia destruktora bez niego:~scoped_ptr() { delete ptr_; }
gdzie
ptr_
jestC*
. Jeśli typC
jest niekompletna w tym momencie, to znaczy, że wszystko jest kompilator wiestruct C;
, to (1) domyślne generowane zrobić-nic destructor służy do instancji wskazał C. Jest mało prawdopodobne, aby to była właściwa czynność w przypadku obiektu zarządzanego przez inteligentny wskaźnik.Jeśli usuwanie przez wskaźnik do niekompletnego typu zawsze miało niezdefiniowane zachowanie, to standard może po prostu wymagać, aby kompilator zdiagnozował to i zakończył się niepowodzeniem. Ale jest dobrze zdefiniowane, gdy prawdziwy destruktor jest trywialny: wiedza, którą programista może mieć, ale kompilator nie ma. Dlaczego język definiuje i na to pozwala, jest poza mną, ale C ++ obsługuje wiele praktyk, które dziś nie są uważane za najlepsze praktyki.
Typ kompletny ma znany rozmiar i dlatego
sizeof(C)
będzie kompilowany wtedy i tylko wtedy, gdyC
jest typem pełnym - ze znanym destruktorem. Więc może być używany jako strażnik. Jeden sposób byłby prosty(void) sizeof(C); // Type must be complete
Ja przypuszczam , że z jakiegoś kompilatora oraz opcje kompilator optymalizuje ją zanim zdążyła zauważyć, że nie należy skompilować i że
enum
jest sposób, aby uniknąć takiego niezgodnych zachowanie kompilatora:enum { type_must_be_complete = sizeof(C) };
Alternatywnym wyjaśnieniem wyboru,
enum
a nie tylko odrzuconego wyrażenia, są po prostu osobiste preferencje.Lub, jak sugeruje James T. Hugget w komentarzu do tej odpowiedzi: „Wyliczenie może być sposobem na utworzenie pseudo-przenośnego komunikatu o błędzie w czasie kompilacji”.
(1) Domyślnie generowany destruktor nic nie rób dla niekompletnego typu był problemem ze starym
std::auto_ptr
. Było tak podstępne, że trafiło do artykułu GOTW na temat idiomu PIMPL , napisanego przez przewodniczącego międzynarodowej komisji normalizacyjnej C ++, Herba Suttera. Oczywiście w dzisiejszych czasach tostd::auto_ptr
jest przestarzałe, zamiast tego użyjemy innego mechanizmu.źródło
sizeof(T)
szacują na 0 dla niekompletnych typów zamiast niepowodzenia kompilacji. Jest to jednak zachowanie niezgodne z zasadami. (2) Od C ++ 11 użyciestatic_assert((sizeof(T) > 0), "T must be a complete type");
byłoby lepszym (i idiomatycznym) rozwiązaniem.sizeof(T)
ocenia do 0 dla niekompletnych typów”.static_assert(sizeof(T) > 0, "…");
w swoich odpowiednich implementacjach of,std::unique_ptr
aby upewnić się, że typ jest kompletny…sizeof(T)
w kontekście boolowskim jest dokładnie równoważne z testowaniemsizeof(T) > 0
, nie ma to większego znaczenia, może poza względami estetycznymi.Może sztuczka, aby mieć pewność,
C
została zdefiniowana.źródło