¿Por qué eliminar explícitamente el constructor?

93

¿Cuándo / por qué querría eliminar explícitamente mi constructor? Suponiendo que la razón es evitar su uso, ¿por qué no hacerlo private?

class Foo
{ 
  public: 
    Foo() = delete; 
};
ceniza
fuente
14
En cierto modo va bien con = default, ni siquiera la clase puede usarlo, y personalmente prefiero ver Uso de la función eliminada. over La función es privada. El primero establece explícitamente "Esto no está destinado a ser utilizado". Si algo sale de eso, la clase que no pueda usarlo realmente hace una diferencia semántica.
chris
15
Sinceramente, creo que la gente está empezando a volverse agresiva con los votos cerrados. No veo cómo esto no es constructivo.
Luchian Grigore
4
@LuchianGrigore: De acuerdo. Me he estado preguntando por qué la comunidad se ha vuelto mucho más rígida. No veo el punto.
Ed S.
11
Dado que rara vez uso C ++ 11, esto es más informativo para mí de lo que probablemente el OP se da cuenta. Ni siquiera sabía que se podía etiquetar a un constructor delete. Tanto la pregunta como la respuesta de Luchian fácilmente se califican como constructivas. Cualquiera que no respire los puntos más finos de C ++ 11, pero lo necesitará pronto, obtendrá algo de ambos.
WhozCraig

Respuestas:

87

Qué tal si:

//deleted constructor
class Foo
{ 
  public: 
    Foo() = delete;     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //illegal
}

versus

//private constructor
class Foo
{ 
  private: 
    Foo() {}     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //legal
}

Básicamente son cosas diferentes. privatele dice que solo los miembros de la clase pueden llamar a ese método o acceder a esa variable (o amigos, por supuesto). En este caso, es legal que un staticmétodo de esa clase (o cualquier otro miembro) llame a un privateconstructor de una clase. Esto no es válido para constructores eliminados.

Muestra aquí .

Luchian Grigore
fuente
3
No necesita declarar Foo () en absoluto, si declara Foo (int). Foo () no se generará y, por lo tanto, Foo f no es válido de todos modos. Entonces, su ejemplo no muestra el caso del constructor eliminado. Véalo
marque el
1
@mark Escribí 2 constructores para probar el punto. Editaré para que quede claro para todos.
Luchian Grigore
1
Entiendo la diferencia, simplemente no entiendo el valor agregado de la declaración de eliminación en general y para un constructor en particular. Después de todo, podría especificar un constructor predeterminado privado sin el cuerpo. Entonces el código también falla, solo durante el enlace. Bueno, puedo ver que eliminar transmite la intención de manera más explícita, pero eso es todo.
marcar el
11
@mark Sí, esa sería la forma de hacer las cosas en C ++ 98. Pero en mi humilde opinión, transmitir la intención claramente es en realidad algo muy importante en la programación en general. En este caso, algunos lectores pueden ver un constructor indefinido privado y asumir que es accidental y simplemente agregar una definición, especialmente si la definición es tan trivial como un constructor predeterminado (Sí, tener un comentario ayuda, pero preferiríamos compilador -aplicación sobre la ejecución de comentarios). Al tener nuestra intención más clara, también obtenemos un mensaje de error mucho mejor que dice "eliminado explícitamente" en lugar de "referencia no definida".
mpark
2
Sinceramente, no entiendo cómo esto responde a la pregunta principal. La pregunta en el título y la primera pregunta de OP en la publicación fue: ¿Cuándo / por qué querría eliminar explícitamente mi constructor?
Alexander Bolinsky
11

¿Por qué eliminar explícitamente el constructor?

Otra razón:
usodelete cuando quiero asegurarme de que se llame a una clase con un inicializador. Lo considero una forma muy elegante de lograr esto sin verificaciones de tiempo de ejecución.

El compilador de C ++ realiza esta comprobación por usted.

class Foo
{
   public:
       Foo() = delete;
       Foo(int bar) : m_bar(bar) {};
   private:
       int m_bar;
}

Este código, muy simplificado , asegura que no hay instanciación como esta:Foo foo;

Peter VARGA
fuente
12
La declaración eliminada no es necesaria aquí. Se elimina automáticamente con cualquier constructor proporcionado por el usuario
Mike Lui
5
Para aclarar el comentario de @MikeLui, la declaración eliminada no es necesaria para el compilador . Hay numerosos casos en los que se debe incluir un código como este para declarar la intención a otros programadores .
Jeff G
Además de declarar su intención, crea un lugar obvio para documentar el motivo de su eliminación en la interfaz pública y, además, el error del compilador será algo breve como "uso de la función eliminada". Si Footuviera numerosos constructores, pero no uno predeterminado, Foo foo;provocaría un error mucho más largo que enumera todos los constructores privados, protegidos y definidos implícitamente que no pudo hacer coincidir.
sigma
Todavía no entiendo cómo la línea adicional con la declaración del constructor que "= eliminar" la palabra clave declara la intención de la idea de "sin constructor predeterminado" mejor que ... ¿simplemente no hay constructor predeterminado? Ejemplo: No quiero declarar la variable "a" en mi código; ¿qué es mejor, escribir "// int a; // no es necesario definir la variable a" o simplemente no escribir nada sobre esta variable en el código?
Ezh
2

Me he encontrado con ctors predeterminados declarados como 'eliminados' en el código fuente de LLVM (en AlignOf.h, por ejemplo). Las plantillas de clases asociadas suelen estar en un espacio de nombres especial llamado 'llvm :: detail'. Creo que todo el propósito allí era que consideraran esa clase solo como una clase de ayuda. Nunca tuvieron la intención de instanciarlos; solo para usarlos dentro del contexto de otras plantillas de clase con algunos trucos de metaprogramación que se ejecutan en tiempo de compilación.

P.ej. existe esta plantilla de clase AlignmentCalcImpl que se usa solo dentro de otra plantilla de clase llamada AlignOf como parámetro para el operador sizeof (.). Esa expresión se puede evaluar en tiempo de compilación; y no hay necesidad de crear una instancia de la plantilla -> entonces, ¿por qué no declarar la eliminación predeterminada de ctor para expresar esta intención?

Pero es solo mi suposición.

gybacsi
fuente