Подтвердить что ты не робот

Является ли законным для вызова delete на пустой указатель неполного типа?

И если да, то почему следующий код дает мне предупреждение

note: ни деструктор, ни оператор-оператор не будут вызваны, даже если они объявлены, когда класс определен

?

struct C;

int main()
{
    C *c = nullptr;

    delete c;

    return 0;
}

Я понимаю, почему это может быть поведение undefined в общем случае, если C имеет нетривиальные/виртуальные деструкторы, но не гарантирует ли стандарт/определяет, что delete на nullptr всегда a noop независимо от ситуации?

Повторить: я спрашиваю конкретно о случае, когда указатель на неполный тип nullptr!

4b9b3361

Ответ 1

В стандарте указано ([expr.delete]/5):

Если удаляемый объект имеет неполный тип класса в точке удаления, а полный класс имеет нетривиальный деструктор или функцию освобождения, поведение undefined.

Итак, если T имеет нетривиальный деструктор или имеет перегрузку operator delete, вы получаете UB. Ничего не сказано о том, что UB основан на значении указателя (то есть: является ли он нулевым указателем или нет).


Что означает "объект, удаляемый"?

Можно считать, что "объект, удаляемый" означает, что это предложение применяется только к вызовам delete для реальных объектов. И поэтому, если вы передаете нулевой указатель, он не применяется.

Во-первых, в остальной части стандартного обсуждения поведения delete явно указано, что его поведение не применяется к нулевым указателям. [expr.delete]/6 & 7 оба начинаются с "Если значение операнда выражения-удаления не является значением нулевого указателя". В пункте 5 явно не содержатся эти слова. Поэтому мы должны предположить, что оно применимо к нулевым указателям.

Во-вторых, каково было бы значение "удаляемого объекта", если оно было передано нулевым указателем? В конце концов, там нет "объекта".

Хорошо, рассмотрим, что означает интерпретировать этот текст, если "объект, удаляемый", говорит конкретно об объекте в конце этого указателя. Что произойдет, если вы удалите массив неполных классов с нетривиальными деструкторами?

В соответствии с этой логикой этот пункт не применяется, является ли указатель нулевым или нет. Зачем? Поскольку "объект, удаляемый", имеет тип массива, а не тип класса. И поэтому этот пункт не может применяться. Это означает, что компилятор должен иметь возможность вызывать delete[] в массиве неполных классов.

Но это невозможно реализовать; это потребует, чтобы компилятор мог отслеживать код, который еще не существует.

Таким образом, либо "удаляемый объект" предназначен для обозначения std::remove_pointer_t<std::decay_t<decltype(expr)>>, либо для стандарта требуется поведение, которое невозможно реализовать. Стандартную формулировку можно было бы немного почистить, заменив "Если удаляемый объект имеет неполный тип класса в точке удаления" с "Если T является указателем на U или массивом U и U имеет неполный тип класса в точке удаления,..."