оператор new() ведет себя по-разному при удалении оператора delete() в зависимости от наличия конструктора по умолчанию

Создание нового объекта класса C с оператором new() дает здесь ошибку:

class C
{
public:
    C() {}
    virtual ~C() {}

    void operator delete(void*) = delete;
};


int main()
{
    C* c = new C;
}

с C2280: 'void C::operator delete(void *)': function was explicitly deleted

Но когда я заменю C() {} на C() = default; или удалю строку, чтобы компилятор вставил конструктор по умолчанию (который, как я полагаю, имеет тот же эффект, что и = default), код скомпилируется и запустится.

Каковы различия между конструктором по умолчанию, сгенерированным компилятором, и конструктором по умолчанию, определяемым пользователем, которые делают это возможным?

У меня есть подсказка в этой публикации, но класс C здесь (без предоставленного пользователем конструктора) не является тривиальным, поскольку деструктор виртуальный, верно?

Скомпилировано с помощью последней версии Visual Studio, c++17.


person yeshjho    schedule 12.02.2020    source источник
comment
Не уверен, но я думаю, что разница в том, что конструктор по умолчанию noexcept   -  person Sebastian Redl    schedule 12.02.2020
comment
Не могу воспроизвести с помощью g++. Аналогичная диагностика относительно operator delete(), написан ли конструктор вручную или неявно сгенерирован. Что согласуется с моими ожиданиями — поскольку выражение new может вызвать исключение, компилятору необходимо получить доступ к operator delete().   -  person Peter    schedule 12.02.2020
comment
@SebastianRedl, вы правы, добавление noexcept заставит код скомпилироваться, но как ...?   -  person yeshjho    schedule 12.02.2020
comment
@Peter Исключение может быть вызвано только конструктором, поэтому, если это noexcept, как упомянул СебастьянРедл, тогда нет необходимости включать вызов operator delete. Также g++ жалуется, только если деструктор виртуальный. В противном случае он всегда компилируется, даже если конструктор вызывает исключение.   -  person walnut    schedule 12.02.2020
comment
@LeDYoM Ваша ссылка посвящена анализу IP-адресов, что, похоже, не имеет отношения к вопросу. Вы разместили неправильную ссылку?   -  person L. F.    schedule 12.02.2020
comment
@ Л.Ф. Ты прав. Отредактировано.   -  person LeDYoM    schedule 12.02.2020
comment
Вот ссылка: godbolt.org/z/8y23BC   -  person LeDYoM    schedule 12.02.2020


Ответы (1)


Каковы различия между конструктором по умолчанию, сгенерированным компилятором, и конструктором по умолчанию, определяемым пользователем, которые делают это возможным?

Выражение new вызывает соответствующее выражение operator new, а затем вызывает конструктор. Если конструктор выдает исключение, выражение new должно отменить действие выражения operator new (во избежание утечки памяти), вызвав соответствующее выражение operator delete. Если последний удален, выражение new не может вызвать его, что приводит к компилятору error: use of deleted function 'static void C::operator delete(void*)'.

Конструктор noexcept не может генерировать исключение, поэтому соответствующий operator delete не нужен, так как он не будет вызываться выражением new. Конструктор default тривиального класса также является конструктором noexcept. Присутствие виртуального деструктора требует, чтобы operator delete не удалялся, потому что специальный скалярный деструктор удаления (деталь реализации для включения выражения delete через указатель базового класса) вызывает operator delete.

Кажется, стандарт C++ не указывает, должен ли компилятор требовать, чтобы operator delete не удалялось, даже если его нельзя вызвать выражением new. gcc, однако, похоже, вообще не вызывает соответствующее выражение operator delete в выражении new, если оно deleted (опубликовал отчет об ошибке).

person Maxim Egorushkin    schedule 12.02.2020