Una función eliminada está implícitamente en línea
(Anexo a las respuestas existentes)
... Y una función eliminada será la primera declaración de la función (excepto para eliminar especializaciones explícitas de plantillas de funciones; la eliminación debe realizarse en la primera declaración de la especialización), lo que significa que no puede declarar una función y luego eliminarla, por ejemplo, en su definición local a una unidad de traducción.
Citando [dcl.fct.def.delete] / 4 :
Una función eliminada está implícitamente en línea. ( Nota: la regla de una definición ( [basic.def.odr] ) se aplica a las definiciones eliminadas. - Nota final ] Una definición eliminada de una función será la primera declaración de la función o, para una especialización explícita de una plantilla de función , la primera declaración de esa especialización. [Ejemplo:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
- ejemplo final )
Se puede especializar una plantilla de función primaria con una definición eliminada
Aunque una regla general es evitar las plantillas de funciones especializadas ya que las especializaciones no participan en el primer paso de la resolución de sobrecarga, existen algunos contextos discutibles en los que puede ser útil. Por ejemplo, cuando se utiliza una plantilla de función primaria no sobrecargada sin definición para que coincida con todos los tipos que a uno no le gustaría convertir implícitamente en una sobrecarga de coincidencia por conversión; es decir, eliminar implícitamente varias coincidencias de conversión implícita implementando solo coincidencias de tipo exacto en la especialización explícita de la plantilla de función primaria no definida y no sobrecargada.
Antes del concepto de función eliminado de C ++ 11, uno podía hacer esto simplemente omitiendo la definición de la plantilla de función primaria, pero esto daba oscuros errores de referencia indefinidos que posiblemente no dieron ninguna intención semántica del autor de la plantilla de función primaria (omitido intencionalmente ?) Si, en cambio, eliminamos explícitamente la plantilla de función primaria, los mensajes de error en caso de que no se encuentre una especialización explícita adecuada se vuelven mucho más agradables, y también muestran que la omisión / eliminación de la definición de la plantilla de función primaria fue intencional.
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t);
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
//use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}
Sin embargo, en lugar de simplemente omitir una definición para la plantilla de función primaria anterior, arrojando un oscuro error de referencia indefinido cuando no hay coincidencias de especialización explícitas, la definición de plantilla primaria se puede eliminar:
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t) = delete;
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
use_only_explicit_specializations(str);
/* error: call to deleted function 'use_only_explicit_specializations'
note: candidate function [with T = std::__1::basic_string<char>] has
been explicitly deleted
void use_only_explicit_specializations(T t) = delete; */
}
Produciendo un mensaje de error más legible, donde la intención de eliminación también es claramente visible (donde un error de referencia indefinido podría hacer que el desarrollador piense que es un error irreflexivo).
Volviendo a por qué querríamos usar esta técnica. Una vez más, especializaciones explícitas podrían ser útiles para implícitamente eliminar las conversiones implícitas.
#include <cstdint>
#include <iostream>
void warning_at_best(int8_t num) {
std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}
template< typename T >
void only_for_signed(T t) = delete;
template<>
void only_for_signed<int8_t>(int8_t t) {
std::cout << "UB safe! 1 byte, " << +t << "\n";
}
template<>
void only_for_signed<int16_t>(int16_t t) {
std::cout << "UB safe! 2 bytes, " << +t << "\n";
}
int main()
{
const int8_t a = 42;
const uint8_t b = 255U;
const int16_t c = 255;
const float d = 200.F;
warning_at_best(a); // 42
warning_at_best(b); // implementation-defined behaviour, no diagnostic required
warning_at_best(c); // narrowing, -Wconstant-conversion warning
warning_at_best(d); // undefined behaviour!
only_for_signed(a);
only_for_signed(c);
//only_for_signed(b);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = unsigned char]
has been explicitly deleted
void only_for_signed(T t) = delete; */
//only_for_signed(d);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = float]
has been explicitly deleted
void only_for_signed(T t) = delete; */
}