¿Por qué las funciones eliminadas de C++ 11 participan en la resolución de sobrecarga?

Resuelto user541686 asked hace 11 años • 2 respuestas

¿Por qué C++ 11 hace que las funciones " deleted" 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?

user541686 avatar Dec 30 '12 03:12 user541686
Aceptado

La mitad del propósito de la = deletesintaxis 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 deletese elimina la función por completo, la sintaxis sería = deleteequivalente 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 deleted. 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 deletefunción d.

= deletesignifica "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 = deletesignifica 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í = deletese 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 = deletesintaxis 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.

Nicol Bolas avatar Dec 29 '2012 20:12 Nicol Bolas

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 ]

Olaf Dietsche avatar Dec 29 '2012 22:12 Olaf Dietsche