¿Por qué las funciones eliminadas de C++ 11 participan en la resolución de sobrecarga?
¿Por qué C++ 11 hace que las funciones " delete
d" participen en la resolución de sobrecarga ?
¿Por qué es esto útil? O en otras palabras, ¿por qué se ocultan en lugar de eliminarse por completo?
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 particular, debe participar en la resolución de la sobrecarga.
La respuesta que citas te da un ejemplo perfecto:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Si delete
se elimina la función por completo, la sintaxis sería = delete
equivalente a esta:
struct onlydouble2 {
onlydouble2(double);
};
Podrías hacer esto:
onlydouble2 val(20);
Este es C++ legal. El compilador examinará todos los constructores; Ninguno de ellos toma un tipo entero directamente. Pero uno de ellos puede tomarlo tras una conversión implícita. Entonces se 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á e inmediatamente emitirá un error, 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.
Me preguntaba por qué el estándar C++ dice = eliminar significa "Prohíbo 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. "Prohíbo 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 luego declare que existe. Y eso simplemente no es lo suficientemente útil como para necesitar su propia gramática.
de lo contrario no hay manera de declarar que el constructor de copias no existe, y su existencia puede causar ambigüedades sin sentido.
El constructor de copias es una función miembro especial. Cada clase siempre tiene un constructor de copia. Así como siempre tienen un operador de asignación de copia, un constructor de movimiento, etc.
Estas funciones existen; la única pregunta es si es legal llamarlos. Si intentas 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, el compilador generará un error. Pero generará un error debido a un identificador no definido , no debido a un error de "la función no existe" (incluso si su compilador lo informa de esa manera). Se llama a varios constructores mediante resolución de sobrecarga, por lo que su "existencia" se maneja en ese sentido.
En todos los casos, hay una función declarada mediante un identificador o un constructor/destructor (también declarado mediante un identificador, solo un identificador de tipo). La sobrecarga de operadores oculta el identificador detrás del azúcar sintáctico, pero sigue ahí.
La especificación C++ no puede manejar el concepto de "función que no existe". Puede manejar una falta de coincidencia de sobrecarga. Puede manejar una ambigüedad de sobrecarga. Pero no sabe lo que no está ahí. Así = delete
se define en términos de "intentos de decir que esto falla", mucho más útiles, en lugar del menos útil "fingir que nunca escribí esta línea".
Y nuevamente, vuelve a leer la primera parte. No puedes 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 obligar al usuario a utilizar ciertos tipos de parámetros, realizar conversiones explícitas, etc. Básicamente, para frustrar las conversiones de tipos implícitas.
Tu sugerencia no haría eso.
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 imponer una inicialización no predeterminada y una inicialización no integral con
struct onlydouble {
onlydouble() = delete; // OK, but redundant
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
— fin del ejemplo ]
[ Ejemplo : Se puede evitar el uso de una clase en ciertas expresiones nuevas mediante el uso de 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[]
— fin del ejemplo ]
[ Ejemplo : Se puede hacer que una clase no se pueda copiar, es decir, que solo se pueda mover, usando definiciones eliminadas del constructor de copia y del operador de asignación de copia, y luego proporcionando definiciones predeterminadas del constructor de movimiento y del 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
- fin del ejemplo ]