especialização explícita da função-membro da classe de template

88

Eu preciso especializar a função de membro do modelo para algum tipo (digamos duplo ). Funciona bem enquanto a classe em Xsi não é uma classe de modelo, mas quando eu faço um modelo, o GCC começa a dar erros de tempo de compilação.

#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();
}

aqui está a mensagem de erro

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()

Como posso corrigir isso e qual é o problema aqui?

Desde já, obrigado.

ledokol
fonte
2
isso é ilegal no padrão atual, para se especializar, você tem que se especializar a classe também ...
Nim
mas funciona se a classe não for um modelo. Isso também é ilegal?
ledokol de
não, isso é perfeitamente normal, é apenas para modelos de classe que esta regra se aplica (AFAIK).
Nim,

Respostas:

108

Não funciona assim. Você precisaria de dizer o seguinte, mas é não correta

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

}

Membros explicitamente especializados precisam que seus modelos de classe ao redor também sejam explicitamente especializados. Portanto, você precisa dizer o seguinte, que só iria especializar o membro para X<int>.

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

}

Se você quiser manter o modelo ao redor não especializado, você tem várias opções. Eu prefiro sobrecargas

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>) {

   }
};
Johannes Schaub - litb
fonte
por que você precisa do type<>invólucro? não poderia uma conversão de 0 para um ponteiro do tipo Tresolver o problema? Eu acho que não é tão elegante ...
Nim
Parece que isso realmente não é possível fazer. Obrigado.
ledokol de
3
@Nim certo, acho que o lançamento de ponteiros é feio e não funcionaria para tipos para os quais você não pode formar ponteiros (referências). Além disso, ter um parâmetro de função como um ponteiro para um tipo de array sem um tamanho é ilegal em C ++. Tê-lo em um wrapper de tipo faz com que funcione para todos os tipos.
Johannes Schaub - litb de
2
@ JohannesSchaub-litb: Quais são as outras opções ao longo das sobrecargas? Pode mostrar alguns deles?
Jean-Bernard Jansen
2
@ fast-reflexes seu comentário foi usar template<typename T> void get_as(T*); void get_as(double*);e passar a (T*)0.
Johannes Schaub - litb
24

Se for possível usar, std::enable_ifpodemos contar com SFINAE (a falha de substituição não é um erro)

que funcionaria assim (veja AO VIVO ):

#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;
}

O ruim é que, com todos esses enable_if's, apenas uma especialização precisa estar disponível para o compilador, caso contrário, ocorrerá um erro de desambiguação. É por isso que o comportamento padrão "get as T" também precisa de um if habilitado.

Gabriel
fonte
Atualizado o post para torná-lo mais moderno.
Gabriel