Закрытое удаление оператора вызывает ошибку времени компиляции с GCC и Clang, но не с MSVC - PullRequest
0 голосов
/ 28 июня 2018

По мотивам этот не очень хорошо заданный дубликат , я считаю, что проблема заслуживает нового отдельного вопроса с четким названием. Следующий код вызывает ошибку компиляции с GCC 8.1.0 и Clang 6.0.0, но не с MSVC 19.00:

class X {
   public:
      X() /* noexcept */ { }    
   private:
      static void operator delete(void*) { }
};

int main() { 
    X* x = new X{}; 
}

С expr.new :

Если какая-либо часть инициализации объекта, описанного выше, завершается выдачей исключения и может быть найдена подходящая функция освобождения, функция освобождения вызывается для освобождения памяти, в которой создавался объект, после чего исключение продолжает распространяться в контексте нового выражения. Если не может быть найдена однозначная совпадающая функция освобождения, распространение исключения не приводит к освобождению памяти объекта. [ Примечание: Это уместно, когда вызываемая функция выделения не выделяет память ; в противном случае это может привести к утечке памяти. - Конечная нота ]

Фактически это не означает, что ошибка компиляции должна быть вызвана, если не найдена соответствующая функция освобождения ::operator delete. Или, сделав его приватным, можно найти что-то вроде , но к нему нельзя получить доступ ? Какие компиляторы правы?

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Здесь есть два вопроса:

  1. Как найти operator delete, хотя это личное?

C ++ сначала пытается найти имя в любом месте ; проверка защиты доступа - более поздняя фаза.
Таким образом, ваш operator delete найден, но недоступен.

  1. Почему мой operator delete должен быть доступен, когда конструктор noexcept?

Формулировка «Если какая-либо часть инициализации объекта [...] завершается с помощью исключения», предполагает, что остальная часть абзаца неприменима из-за noexcept.

Однако, как предполагает «любая часть ...», могут быть исключения между выделением и входом в конструктор (при оценке инициализаторов) или после выхода из конструктора (при уничтожении инициализаторов).

Рассмотрим

struct Y
{
    Y() {}
    Y(const Y&) { throw "sorry"; }
};

class X {
   public:
      X(Y y) noexcept { }    
   private:
      static void operator delete(void*) { }
};

int main() { 
    Y y;
    X* x = new X{y}; 
}

где конструктор копирования Y выдает перед вводом конструктора X, но после выделения, поэтому память должна быть освобождена.

Так что я думаю, что Visual C ++ не прав (снова).

0 голосов
/ 28 июня 2018

Visual studio работает со спецификатором noexcept странно. На бумаге это не должно строиться. Причина в том, что функция decllocation ищется независимо от функции размещения.

[expr.new] / 20, 21 и 22 * ​​1007 *

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

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

Если выражение new начинается с унарного оператора ::​, Имя функции освобождения ищется в глобальной области видимости. В противном случае, если выделенный тип является типом класса T или массивом поэтому имя функции освобождения ищется в области T. Если этот поиск не может найти имя, или если выделенный тип не тип класса или его массив, имя функции освобождения посмотрел в глобальном масштабе.

Согласно p20, функция освобождения должна искать , так как мы создаем объект класса. Тогда функция deallcoation найдена успешно и однозначна (это член). Поскольку спецификаторы доступа проверяются только после поиска имени, это должно вызвать ошибку. GCC и Clang верны.

...