#cpp #language_lawyer #неопределенное_поведение
В чём разница между этими действиями? static void operator delete (void *p) { ::delete p; } static void operator delete (void *p) { ::operator delete(p); } Кажется, что всё работает в обоих случаях: https://ideone.com/cQ8lTJ #includeusing namespace std; struct a { static void operator delete (void *p) { ::delete p; } }; struct b { static void operator delete (void *p) { ::operator delete(p); } }; int main() { delete new a(); delete new b(); cout << "Done :)" << endl; return 0; } Но если добраться до предупреждений компилятора https://ideone.com/bN3XOh prog.cpp: In static member function ‘static void a::operator delete(void*)’: prog.cpp:5:62: warning: deleting ‘void*’ is undefined [-Wdelete-incomplete] struct a { static void operator delete (void *p) { ::delete p; } }; ^ то возникает ощущение, что он предупреждает о UB в первом варианте. Действительно ли это UB? Если да, то почему это всего лишь предупреждение, а не ошибка? PS: Из похожего нашёл такой вопрос, но там про внутреннее устройство вызова delete, причём не показывается, почему именно код из моего вопроса неверный.
Ответы
Ответ 1
operator delete - это функция. ::operator delete(p); - это вызов этой функции. Выражение delete p; - это вызов деструктора, поиск указателя на полный объект и вызов функции operator delete для этого полного объекта. Операция delete для void* не имеет смысла, о чем и говорит компилятор. В контексте другой функции operator delete оно особенно не имеет смысла. Более подробно про выражение delete и функции operator delete можно почитать тут.Ответ 2
В качестве небольшого дополнения к ответу @Abyx: Согласно стандарту, 8.5.2.5/1: The operand shall be of pointer to object type or of class type. If of class type, the operand is contextually implicitly converted to a pointer to object type.⁸² ⁸²⁾ This implies that an object cannot be deleted using a pointer of type void* because void is not an object type. Таким образом, использование ::delete p; (первый вариант) запрещено стандартом, и ведёт таким образом к UB. Почему это объявлено UB, а не ошибкой компиляции, мне сложно судить. Ещё одна причина, по которой такой вызов проблематичен: (стандарт, 8.5.2.5/1): In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.Ответ 3
Немного кода для иллюстрации ответа @Abyx'а #include#include using namespace std; class Foo { public: Foo (const std::string &_str): str(_str) { std::cout << str << "::" << __func__<< '\n'; } ~Foo () { std::cout << str << "::" << __func__<< '\n'; } static void* operator new (size_t sz) { std::cout << __func__<< " sz(" << sz << ')' << '\n'; return ::operator new(sz); } static void operator delete (void* p) { std::cout << __func__ << '\n'; return ::operator delete(p); } std::string str; }; int main(int /*argc*/, char* /*argv*/[]) { Foo *foo = new Foo("foo1"); delete foo; std::cout << '\n'; foo = new Foo("foo2"); Foo::operator delete(foo); return 0; } Вывод: operator new sz(32) foo1::Foo foo1::~Foo operator delete operator new sz(32) foo2::Foo operator delete т.е. Как и ожидается operator delete не вызывает деструктор. Ответ 4
::delete p; и ::operator delete(p); - две альтернативных записи вызова глобального оператора delete. А предупреждение компилятора справедливо вызвано попыткой удаления указателя на незавершенный тип (incomplete type). Однако неопределенное поведение конкретно в этом случае заключается не в вызове delete именно для типа void* (хотя он и является незавершенным), а в том, что тип, на который указывает указатель, не является непосредственно типом или одним из подтипов типа, который был и использован при вызове оператора new, создавшего этот объект. Причем диагностического сообщения в этом случае вообще не требуется. Скорее всего, в компиляторе еще не реализовали дополнительные диагностики для вызова delete с использованием синтаксиса функции, так как такое встречается сравнительно редко.
Комментариев нет:
Отправить комментарий