¿Por qué C ++ 11 hace que las funciones " delete
d" participen en la resolución de sobrecargas ?
¿Por qué es útil esto? O en otras palabras, ¿por qué están ocultos en lugar de eliminarse por completo?
¿Por qué C ++ 11 hace que las funciones " delete
d" participen en la resolución de sobrecargas ?
¿Por qué es útil esto? O en otras palabras, ¿por qué están ocultos en lugar de eliminarse por completo?
Respuestas:
La mitad del propósito de la = delete
sintaxis es poder evitar que las personas llamen a ciertas funciones con ciertos parámetros. Esto es principalmente para evitar conversiones implícitas en ciertos escenarios específicos. Para prohibir una sobrecarga en particular, debe participar en la resolución de la sobrecarga.
La respuesta que cita le da un ejemplo perfecto:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Si se delete
elimina la función por completo, la = delete
sintaxis sería equivalente a esto:
struct onlydouble2 {
onlydouble2(double);
};
Podrías hacer esto:
onlydouble2 val(20);
Esto es C ++ legal. El compilador examinará todos los constructores; ninguno de ellos toma directamente un tipo entero. Pero uno de ellos puede tomarlo después de una conversión implícita. Entonces lo llamará así.
onlydouble val(20);
Esto no es C ++ legal. El compilador examinará todos los constructores, incluidos los delete
d. Verá una coincidencia exacta, vía std::intmax_t
(que coincidirá exactamente con cualquier literal entero). Entonces, el compilador lo seleccionará y luego emitirá un error inmediatamente, porque seleccionó una delete
función d.
= delete
significa "prohíbo esto", no simplemente "esto no existe". Es una declaración mucho más fuerte.
Estaba preguntando por qué el estándar C ++ dice = eliminar significa "Prohibo esto" en lugar de "esto no existe"
Es porque no necesitamos una gramática especial para decir "esto no existe". Obtenemos esto implícitamente simplemente no declarando el "esto" particular en cuestión. "Prohibo esto" representa una construcción que no se puede lograr sin una gramática especial. Entonces obtenemos una gramática especial para decir "prohíbo esto" y no lo otro.
La única funcionalidad que obtendría al tener una gramática explícita de "esto no existe" sería evitar que alguien más tarde declare que existe. Y eso no es lo suficientemente útil como para necesitar su propia gramática.
De lo contrario, no hay forma de declarar que el constructor de copia no existe, y su existencia puede causar ambigüedades sin sentido.
El constructor de copia es una función miembro especial. Cada clase siempre tiene un constructor de copias. Del mismo modo que siempre tienen un operador de asignación de copia, un constructor de movimiento, etc.
Estas funciones existen; la pregunta es solo si es legal llamarlos. Si intenta decir que eso = delete
significa que no existen, entonces la especificación tendría que explicar qué significa que una función no exista. Este no es un concepto que maneja la especificación.
Si intenta llamar a una función que aún no ha sido declarada / definida, entonces el compilador producirá un error. Pero se producirá un error debido a un identificador indefinido , no debido a un error de "la función no existe" (incluso si su compilador lo informa de esa manera). Todos los constructores son llamados por resolución de sobrecarga, por lo que su "existencia" se maneja en ese sentido.
En todos los casos, hay una función declarada a través de un identificador, o un constructor / destructor (también declarado a través de un identificador, solo un identificador de tipo). La sobrecarga del operador oculta el identificador detrás del azúcar sintáctico, pero sigue ahí.
La especificación C ++ no puede manejar el concepto de una "función que no existe". Puede manejar un desajuste de sobrecarga. Puede manejar una ambigüedad de sobrecarga. Pero no sabe lo que no está. Así que = delete
se define en términos de los "intentos de llamar a esto fallidos" mucho más útiles que los menos útiles "pretenden que nunca escribí esta línea".
Y nuevamente, vuelva a leer la primera parte. No puede hacer eso con "la función no existe". Esa es otra razón por la que se define de esa manera: porque uno de los principales casos de uso de la = delete
sintaxis es poder forzar al usuario a usar ciertos tipos de parámetros, a emitir explícitamente, etc. Básicamente, para frustrar las conversiones de tipos implícitas.
Tu sugerencia no haría eso.
= delete
significar "este miembro no existe", lo que implicaría que no podría participar en la resolución de sobrecarga.
= delete
significa "este miembro no existe", entonces el primer ejemplo que publiqué no podría evitar que las personas pasen enteros al onlydouble
constructor de , ya que la onlydouble
sobrecarga que se elimina no existiría . No participaría en la resolución de sobrecarga y, por lo tanto, no le impediría pasar enteros. Que es la mitad del punto de la = delete
sintaxis: poder decir, "No se puede pasar X implícitamente a esta función".
=delete
? Después de todo, podemos decir "no copiable" haciendo exactamente lo mismo: declarar el constructor de copia / asignación privada. Además, tenga en cuenta que declarar algo privado no lo convierte en incalculable; el código dentro de la clase todavía puede llamarlo. Entonces no es lo mismo que = delete
. No, la = delete
sintaxis nos permite hacer algo que antes era muy inconveniente e inescrutable de una manera mucho más obvia y razonable.
El Borrador de trabajo de C ++ 2012-11-02 no proporciona una justificación detrás de esta regla, solo algunos ejemplos
8.4.3 Definiciones eliminadas [dcl.fct.def.delete]
...
3 [ Ejemplo : Se puede hacer cumplir la inicialización no predeterminada y la inicialización no integral con
struct onlydouble {
onlydouble() = delete; // OK, but redundant
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
- end example ]
[ Ejemplo : Se puede evitar el uso de una clase en ciertas expresiones nuevas usando definiciones eliminadas de un operador nuevo declarado por el usuario para esa clase.
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
- end example ]
[ Ejemplo : Se puede hacer que una clase no se pueda copiar, es decir, solo mover, utilizando definiciones eliminadas del constructor de copia y el operador de asignación de copia, y luego proporcionando definiciones predeterminadas del constructor de movimiento y el operador de asignación de movimiento.
struct moveonly {
moveonly() = default;
moveonly(const moveonly&) = delete;
moveonly(moveonly&&) = default;
moveonly& operator=(const moveonly&) = delete;
moveonly& operator=(moveonly&&) = default;
~moveonly() = default;
};
moveonly *p;
moveonly q(*p); // error, deleted copy constructor
- ejemplo final ]