Specializzazione del modello C ++ senza funzione predefinita
-
06-07-2019 - |
Domanda
Ho il seguente codice che compila e funziona bene:
template<typename T>
T GetGlobal(const char *name);
template<>
int GetGlobal<int>(const char *name);
template<>
double GetGlobal<double>(const char *name);
Comunque voglio rimuovere il " default " funzione. Cioè, voglio fare tutte le chiamate a GetGlobal & Lt; t & Gt; dove 't' non è un int o un doppio un errore.
Ad esempio, GetGlobal < char > () dovrebbe essere un errore di tempo di compilazione.
Ho provato a cancellare la funzione predefinita, ma, come immaginavo, ho ricevuto molti errori. Quindi c'è un modo per " disabilitare " e consentire chiamate solo alle versioni specializzate della funzione?
Grazie!
Soluzione
Per ottenere un errore di compilazione, implementalo come:
template<typename T>
T GetGlobal(const char *name) { T::unimplemented_function; }
// `unimplemented_function` identifier should be undefined
Se usi Boost potresti renderlo più elegante:
template<typename T>
T GetGlobal(const char *name) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
Lo standard C ++ garantisce che non esiste un tipo di questo tipo con dimensioni pari a 0, quindi verrà visualizzato un errore di compilazione.
Come sbi ha suggerito nei suoi commenti che l'ultimo potrebbe essere ridotto a:
template<typename T>
T GetGlobal(const char *name) { char X[!sizeof(T)]; }
Preferisco la prima soluzione, perché fornisce un messaggio di errore più chiaro (almeno in Visual C ++) rispetto agli altri.
Altri suggerimenti
Sebbene si tratti di una domanda vecchia e obsoleta, vale la pena notare che C++11
aveva risolto questo problema utilizzando le funzioni eliminate:
template<typename T>
T GetGlobal(const char *name) = delete;
template<>
int GetGlobal<int>(const char *name);
Aggiorna
Questo non verrà compilato in MacOS llvm 8
.
È dovuto a un difetto di 4 anni ancora sospeso (vedere questa segnalazione di bug ).
La seguente soluzione alternativa si adatterà al problema (usando un static_assert
costrutto).
template<typename T>
T GetGlobal(const char *name) {
static_assert(sizeof(T) == 0, "Only specializations of GetGlobal can be used");
}
template<>
int GetGlobal<int>(const char *name);
Aggiorna
Visual Studio 15.9 ha lo stesso bug. Usa la soluzione precedente per questo.
Se non lo implementi, otterrai almeno un errore del linker. Se desideri un errore di compilazione, puoi farlo con i modelli di classe:
template<typename T>
struct GlobalGetter;
template<>
struct GlobalGetter<int> {
static int GetGlobal(const char *name);
};
template<>
struct GlobalGetter<double> {
static double GetGlobal(const char *name);
};
template<typename T>
T GetGlobal(const char *name)
{
return GlobalGetter<T>::GetGlobal(name);
}
Suggerirei di non fornire effettivamente un'implementazione, ma solo una nuda dichiarazione del metodo.
L'altra opzione sarebbe quella di utilizzare un'asserzione in fase di compilazione. Boost ha un certo numero di tali animali.
namespace mpl = boost::mpl;
BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>,
boost::same_type<T, int> >));
Esiste anche la sua controparte della versione del messaggio, che sarebbe di aiuto.
Le seguenti sono tecniche alternative all'utilizzo di boost:
Dichiara un typedef con un nome dipendente
Funziona perché la ricerca del nome per DONT si verifica solo quando 'T' è stato sostituito. Questa è una versione simile (ma legale) dell'esempio fornito da Kirill
template <typename T>
T GetGlobal (const char * name) {
typedef typename T::DONT CALL_THIS_FUNCTION;
}
Utilizza un tipo di ritorno incompleto
Questa tecnica non funziona per le specializzazioni, ma funzionerà per i sovraccarichi. L'idea è che è legale dichiarare una funzione che restituisce un tipo incompleto, ma non chiamarla:
template <typename T>
class DONT_CALL_THIS_FUNCTION GetGlobal (const char * name);