specializzazione esplicita della funzione del membro della classe template


88

Ho bisogno di specializzare la funzione membro del modello per qualche tipo (diciamo double ). Funziona bene mentre la classe Xstessa non è una classe modello, ma quando lo creo modello GCC inizia a dare errori in fase di compilazione.

#include <iostream>
#include <cmath>

template <class C> class X
{
public:
   template <class T> void get_as();
};

template <class C>
void X<C>::get_as<double>()
{

}

int main()
{
   X<int> x;
   x.get_as();
}

ecco il messaggio di errore

source.cpp:11:27: error: template-id
  'get_as<double>' in declaration of primary template
source.cpp:11:6: error: prototype for
  'void X<C>::get_as()' does not match any in class 'X<C>'
source.cpp:7:35: error: candidate is:
  template<class C> template<class T> void X::get_as()

Come posso risolverlo e qual è il problema qui?

Grazie in anticipo.


2
questo è illegale nello standard attuale, per specializzarti, devi specializzare anche la classe ...
Nim

ma funziona se la classe non è un modello. È anche illegale?
ledokol

no, va benissimo, è solo per i modelli di classe che si applica questa regola (AFAIK).
Nim

Risposte:


108

Non funziona in questo modo. Si avrebbe bisogno di dire quanto segue, ma è non è corretto

template <class C> template<>
void X<C>::get_as<double>()
{

}

I membri esplicitamente specializzati hanno bisogno che anche i loro modelli di classe circostanti siano esplicitamente specializzati. Quindi devi dire quanto segue, che specializzerebbe solo il membro per X<int>.

template <> template<>
void X<int>::get_as<double>()
{

}

Se vuoi mantenere il modello circostante non specializzato, hai diverse scelte. Preferisco i sovraccarichi

template <class C> class X
{
   template<typename T> struct type { };

public:
   template <class T> void get_as() {
     get_as(type<T>());
   }

private:
   template<typename T> void get_as(type<T>) {

   }

   void get_as(type<double>) {

   }
};

perché hai bisogno del type<>wrapper? non potrebbe un cast di uno 0 a un puntatore di tipo Tfare il trucco? Immagino non sia così elegante ...
Nim

Sembra che questo non sia davvero possibile. Grazie.
ledokol

3
@Nim ha ragione, penso che la cosa del cast del puntatore sia brutta e non funzionerebbe per i tipi a cui non puoi formare puntatori (riferimenti). Inoltre, avere un parametro di funzione come puntatore a un tipo di matrice senza una dimensione è illegale in C ++. Averlo in un wrapper di tipo lo fa funzionare per tutti i tipi.
Johannes Schaub - litb

2
@ JohannesSchaub-litb: Quali sono le altre scelte rispetto ai sovraccarichi? Può mostrarne alcuni?
Jean-Bernard Jansen

2
@ fast-reflexes il suo commento è stato quello di usare template<typename T> void get_as(T*); void get_as(double*);e passare a (T*)0.
Johannes Schaub - litb

25

Se uno è in grado di usarlo, std::enable_ifpossiamo fare affidamento su SFINAE (la mancata sostituzione non è un errore)

funzionerebbe così (vedi LIVE ):

#include <iostream>
#include <type_traits>

template <typename C> class X
{
public:
    template <typename T, 
              std::enable_if_t<!std::is_same_v<double,T>, int> = 0> 
    void get_as() { std::cout << "get as T" << std::endl; }

    template <typename T, 
              std::enable_if_t<std::is_same_v<double,T>, int> = 0> 
    void get_as() { std::cout << "get as double" << std::endl; }
};

int main() {
   X<int> d;
   d.get_as<double>();

   return 0;
}

La cosa brutta è che, con tutte queste abilitazioni, deve essere disponibile solo una specializzazione per il compilatore, altrimenti si verificherà un errore di disambiguazione. Ecco perché il comportamento predefinito "ottieni come T" necessita anche di un'abilitazione se.


Aggiornato il post per renderlo più moderno.
Gabriel
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.