В настоящее время я читаю исходный код Protocol Buffer
и обнаружил один странный enum
код, определенный здесь
~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;
}
}
Почему enum { type_must_be_complete = sizeof(C) };
определяется здесь? для чего это используется?
ptr_
себя вsizeof
assizeof(*ptr_)
вместоsizeof(C)
.Ответы:
Этот трюк позволяет избежать UB, гарантируя, что определение C доступно при компиляции этого деструктора. В противном случае компиляция не удалась бы, поскольку
sizeof
неполный тип (объявленные вперед типы) нельзя определить, но можно использовать указатели.В скомпилированном двоичном коде этот код будет оптимизирован и не будет иметь никакого эффекта.
Обратите внимание, что: Удаление неполного типа может быть неопределенным поведением из 5.3.5 / 5 :.
g++
даже выдает следующее предупреждение:источник
sizeof(C)
завершится ошибкой во время компиляции, еслиC
это не полный тип. Установка локальной области видимостиenum
делает оператор безопасным во время выполнения.Это способ программиста защитить себя от самих себя: поведение последующего
delete ptr_
на неполном типе не определено, если у него есть нетривиальный деструктор.источник
delete
? И если да, то почему компилятор все равно его не улавливает?C = void
? Если бы этоC
был просто неопределенный тип, развеdelete
оператор не завершился бы ошибкой?Чтобы понять это
enum
, начните с рассмотрения деструктора без него:~scoped_ptr() { delete ptr_; }
где
ptr_
аC*
. Если типC
на этом этапе является неполным, то есть все, что известно компиляторуstruct C;
, то (1) для указанного экземпляра C используется деструктор, созданный по умолчанию . Вряд ли это будет правильным решением для объекта, управляемого интеллектуальным указателем.Если удаление с помощью указателя на неполный тип всегда имело неопределенное поведение, тогда стандарт мог бы просто потребовать, чтобы компилятор диагностировал его и завершился ошибкой. Но это четко определено, когда реальный деструктор тривиален: знания, которые может иметь программист, но нет у компилятора. Я не понимаю, почему язык определяет и допускает это, но C ++ поддерживает многие практики, которые сегодня не считаются лучшими.
Полный тип имеет известный размер и, следовательно,
sizeof(C)
будет компилироваться тогда и только тогда, когдаC
это полный тип - с известным деструктором. Так что его можно использовать как охранник. Один путь был бы просто(void) sizeof(C); // Type must be complete
Я предполагаю, что с некоторым компилятором и параметрами компилятор оптимизирует его, прежде чем он сможет заметить, что он не должен компилироваться, и что
enum
это способ избежать такого несоответствующего поведения компилятора:enum { type_must_be_complete = sizeof(C) };
Альтернативное объяснение выбора,
enum
а не просто отброшенное выражение, - это просто личные предпочтения.Или, как предлагает Джеймс Т. Хаггет в комментарии к этому ответу, «перечисление может быть способом создания псевдопереносимого сообщения об ошибке во время компиляции».
(1) Сгенерированный по умолчанию деструктор бездействия для неполного типа был проблемой со старым
std::auto_ptr
. Он был настолько коварен, что попал в статью GOTW об идиоме PIMPL , написанную председателем международного комитета по стандартизации C ++ Хербом Саттером. Конечно, в настоящее время этоstd::auto_ptr
не рекомендуется, вместо этого можно использовать какой-то другой механизм.источник
sizeof(T)
оценку 0 для неполных типов вместо сбоя компиляции. Однако это некорректное поведение. (2) Начиная с C ++ 11, использованиеstatic_assert((sizeof(T) > 0), "T must be a complete type");
было бы превосходным (и идиоматическим) решением.sizeof(T)
оценка для неполных типов равна 0».static_assert(sizeof(T) > 0, "…");
в своих соответствующих реализациях,std::unique_ptr
чтобы убедиться, что тип является полным…sizeof(T)
в логическом контексте в точности эквивалентна тестированиюsizeof(T) > 0
, на самом деле это не имеет значения, за исключением, возможно, эстетических соображений.Может быть, уловка
C
определена.источник