El operador new () se comporta de manera diferente cuando el operador delete () se elimina según la existencia del constructor predeterminado

17

Crear un nuevo objeto de clase C con el operador new () da un error aquí:

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

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


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

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

Pero cuando sustituyo C() {} con C() = default; o elimino la línea para que el compilador inserta un constructor por defecto (que creo que tiene el mismo efecto con = default), el código se compila y corre.

¿Cuáles son las diferencias entre el constructor predeterminado generado por el compilador y el constructor predeterminado definido por el usuario que hacen que esto suceda?

Tengo alguna pista en esta publicación , pero la clase C aquí (sin el constructor proporcionado por el usuario) no es trivial ya que el destructor es virtual, ¿verdad?

Compilado con la última versión de Visual Studio, c ++ 17.

yeshjho
fuente
3
No estoy seguro, pero creo que la diferencia es que el constructor predeterminado esnoexcept
Sebastian Redl
1
No se puede reproducir con g ++. Diagnóstico similar sobre operator delete()si el constructor se escribe manualmente o se genera implícitamente Lo cual es coherente con mis expectativas: dado que la newexpresión puede generar una excepción , el compilador debe acceder operator delete().
Peter
@SebastianRedl tienes razón, agregar noexcepthará que el código se compile, pero ¿cómo ...?
yeshjho
1
@ Peter La excepción solo puede ser lanzada por el constructor, por lo que si es noexceptcomo SebastianRedl mencionó, entonces operator deleteno es necesario incluir una llamada . Además, g ++ solo se queja si el destructor es virtual. De lo contrario, siempre se compila, incluso si el constructor está lanzando.
nogal
@LeDYoM Su enlace se trata de analizar direcciones IP, lo que parece ser irrelevante para la pregunta. ¿Publicaste un enlace incorrecto?
LF

Respuestas:

17

¿Cuáles son las diferencias entre el constructor predeterminado generado por el compilador y el constructor predeterminado definido por el usuario que hacen que esto suceda?

newLa expresión invoca el correspondiente operator newy luego llama al constructor. Si el constructor lanza una newexpresión de excepción , debe deshacer el efecto de operator new(para evitar pérdidas de memoria) llamando al correspondiente operator delete. Si se elimina esta última, la newexpresión no puede llamarlo, lo que da como resultado el compilador error: use of deleted function 'static void C::operator delete(void*)'.

Un noexceptconstructor no puede lanzar una excepción, por lo tanto, el correspondiente operator deleteno es necesario ya que no será invocado por una newexpresión. Un defaultconstructor de una clase trivial también es un noexceptconstructor. La presencia de un destructor virtual requiere operator deleteque no se elimine porque se invoca el destructor de eliminación escalar especial (un detalle de implementación para permitir la deleteexpresión a través del puntero de la clase base) operator delete.

Parece que el estándar C ++ no especifica si el compilador debe requerir operator deleteque no se elimine, incluso si no es posible llamarlo por newexpresión. gccSin embargo, no parece ser la invocación de la correspondiente operator deleteen newla expresión en absoluto si es deleted (publicado un informe de error ).

Maxim Egorushkin
fuente