Una funzione eliminata è implicitamente in linea
(Addendum alle risposte esistenti)
... E una funzione eliminata deve essere la prima dichiarazione della funzione (ad eccezione dell'eliminazione delle specializzazioni esplicite dei modelli di funzione - l'eliminazione dovrebbe avvenire alla prima dichiarazione della specializzazione), il che significa che non è possibile dichiarare una funzione e successivamente eliminarla, ad esempio, nella sua definizione locale a un'unità di traduzione.
Citando [dcl.fct.def.delete] / 4 :
Una funzione eliminata è implicitamente in linea. ( Nota: la regola di una definizione ( [basic.def.odr] ) si applica alle definizioni eliminate. - Nota finale ] Una definizione eliminata di una funzione deve essere la prima dichiarazione della funzione o, per una specializzazione esplicita di un modello di funzione , la prima dichiarazione di tale specializzazione. [Esempio:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
- fine esempio )
Un modello di funzione principale con una definizione eliminata può essere specializzato
Sebbene una regola empirica generale sia quella di evitare la specializzazione dei modelli di funzioni in quanto le specializzazioni non partecipano al primo passo della risoluzione del sovraccarico, ci sono alcuni contesti in cui può essere utile. Ad esempio, quando si utilizza un modello di funzione principale non sovraccaricato senza definizione per abbinare tutti i tipi che non si vorrebbe convertire implicitamente in un sovraccarico di corrispondenza per conversione altrimenti; cioè, rimuovere implicitamente un numero di corrispondenze di conversione implicita implementando solo corrispondenze esatte di tipo nella specializzazione esplicita del modello di funzione principale non definito e non sovraccaricato.
Prima del concetto di funzione eliminata di C ++ 11, si poteva farlo semplicemente omettendo la definizione del modello di funzione principale, ma ciò dava errori di riferimento oscuri e indefiniti che probabilmente non davano alcun intento semantico all'autore del modello di funzione principale (omesso intenzionalmente ?). Se invece eliminiamo esplicitamente il modello di funzione principale, i messaggi di errore nel caso in cui non venga trovata una specializzazione esplicita adeguata diventano molto più piacevoli e mostrano anche che l'omissione / cancellazione della definizione del modello di funzione principale era intenzionale.
#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< ...
}
Tuttavia, invece di omettere semplicemente una definizione per il modello di funzione principale sopra, producendo un oscuro errore di riferimento indefinito quando nessuna specializzazione esplicita corrisponde, la definizione del modello primario può essere eliminata:
#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; */
}
Produrre un messaggio di errore più leggibile, in cui l'intento di eliminazione è anche chiaramente visibile (in cui un errore di riferimento indefinito potrebbe indurre lo sviluppatore a pensare che si tratti di un errore spiacevole).
Tornando al perché dovremmo mai voler usare questa tecnica? Anche in questo caso, specializzazioni esplicite potrebbero essere utili per implicitamente rimuovere le conversioni implicite.
#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; */
}