spesialisasi eksplisit dari fungsi anggota kelas template

88

Saya perlu mengkhususkan fungsi anggota template untuk beberapa jenis (katakanlah ganda ). Ini berfungsi dengan baik sementara kelas Xitu sendiri bukan kelas template, tetapi ketika saya membuatnya, template GCC mulai memberikan kesalahan waktu kompilasi.

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

ini pesan kesalahannya

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

Bagaimana cara memperbaikinya dan apa masalahnya di sini?

Terima kasih sebelumnya.

ledokol
sumber
2
ini ilegal dalam standar saat ini, untuk mengkhususkan, Anda harus mengkhususkan kelas juga ...
Nim
tetapi berfungsi jika kelasnya bukan template. Apakah itu ilegal juga?
ledokol
tidak, tidak apa-apa, hanya untuk template kelas yang aturan ini berlaku (AFAIK).
Nim

Jawaban:

108

Tidak seperti itu. Anda perlu mengatakan yang berikut, tetapi itu tidak benar

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

}

Anggota yang terspesialisasi secara eksplisit juga memerlukan templat kelas sekitarnya untuk dikhususkan secara eksplisit. Jadi, Anda perlu mengatakan hal berikut, yang hanya akan mengkhususkan anggota untuk X<int>.

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

}

Jika Anda ingin mempertahankan template sekitarnya tidak terspesialisasi, Anda memiliki beberapa pilihan. Saya lebih suka kelebihan beban

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
sumber
mengapa Anda membutuhkan type<>pembungkusnya? tidak bisakah pemeran 0 ke penunjuk tipe Tberhasil? Saya kira itu tidak se-elegan ...
Nim
Sepertinya ini benar-benar tidak mungkin dilakukan. Terima kasih.
ledokol
3
@Nim benar, saya pikir hal yang dilemparkan penunjuk jelek, dan tidak akan berfungsi untuk jenis yang Anda tidak dapat membentuk penunjuk ke (referensi). Selain itu, memiliki parameter fungsi sebagai penunjuk ke tipe array tanpa ukuran adalah ilegal di C ++. Memiliki itu di pembungkus tipe membuatnya berfungsi untuk semua jenis.
Johannes Schaub - litb
2
@ JohannesSchaub-litb: Apa saja pilihan lain selain kelebihan beban? Bisakah menunjukkan beberapa dari mereka?
Jean-Bernard Jansen
2
@ fast-reflexes komentarnya adalah untuk menggunakan template<typename T> void get_as(T*); void get_as(double*);dan lulus a (T*)0.
Johannes Schaub - litb
25

Jika ada yang bisa digunakan std::enable_ifkita bisa mengandalkan SFINAE (kegagalan substitusi bukan kesalahan)

itu akan bekerja seperti itu (lihat LANGSUNG ):

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

Yang jelek adalah, dengan semua ini hanya satu spesialisasi enable_if yang perlu tersedia untuk kompiler jika tidak, kesalahan disambiguasi akan muncul. Itulah mengapa perilaku default "get as T" juga membutuhkan pengaktifan jika.

Gabriel
sumber
Memperbarui postingan agar lebih modern.
Gabriel