explizite Spezialisierung der Elementfunktion der Vorlagenklasse

85

Ich muss die Vorlagenelementfunktion für einen Typ spezialisieren (sagen wir doppelt ). Es funktioniert gut, während die Klasse Xselbst keine Vorlagenklasse ist, aber wenn ich sie als Vorlage erstelle, gibt GCC Fehler bei der Kompilierung aus.

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

Hier ist die Fehlermeldung

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

Wie kann ich das beheben und was ist das Problem hier?

Danke im Voraus.

Ledokol
quelle
2
Dies ist im aktuellen Standard illegal. Um sich zu spezialisieren, muss man sich auch auf die Klasse spezialisieren ...
Nim
aber es funktioniert, wenn die Klasse keine Vorlage ist. Ist es auch illegal?
Ledokol
Nein, das ist vollkommen in Ordnung. Diese Regel gilt nur für Klassenvorlagen (AFAIK).
Nim

Antworten:

103

So funktioniert das nicht. Sie müssten Folgendes sagen, aber es ist nicht korrekt

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

}

Explizit spezialisierte Mitglieder müssen ihre umgebenden Klassenvorlagen ebenfalls explizit spezialisiert haben. Sie müssen also Folgendes sagen, für das sich das Mitglied nur spezialisieren würde X<int>.

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

}

Wenn Sie die umgebende Vorlage nicht spezialisiert haben möchten, haben Sie mehrere Möglichkeiten. Ich bevorzuge Überladungen

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
quelle
Warum brauchst du den type<>Wrapper? Konnte eine Umwandlung einer 0 in einen Zeiger vom Typ Tnicht den Trick machen? Ich denke, es ist nicht so elegant ...
Nim
Sieht so aus, als wäre das wirklich nicht möglich. Vielen Dank.
Ledokol
3
@Nim richtig, ich denke, das Zeiger-Casting-Ding ist hässlich und würde nicht für Typen funktionieren, auf die man keine Zeiger bilden kann (Referenzen). Außerdem ist es in C ++ unzulässig, wenn ein Funktionsparameter ein Zeiger auf einen Array-Typ ohne Größe ist. Wenn Sie es in einem Typ-Wrapper haben, funktioniert es für alle Typen.
Johannes Schaub - litb
2
@ JohannesSchaub-litb: Was sind die anderen Möglichkeiten bei Überlastungen? Können einige von ihnen zeigen?
Jean-Bernard Jansen
2
@ Fast-Reflexe Sein Kommentar war, ein zu verwenden template<typename T> void get_as(T*); void get_as(double*);und zu übergeben (T*)0.
Johannes Schaub - Litb
22

Wenn man es benutzen kann, können std::enable_ifwir uns auf SFINAE verlassen (Substitutionsfehler sind kein Fehler)

das würde so funktionieren (siehe 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;
}

Das Hässliche ist, dass bei all diesen enable_ifs nur eine Spezialisierung für den Compiler verfügbar sein muss, da sonst ein Disambiguierungsfehler auftritt. Deshalb benötigt das Standardverhalten "get as T" auch eine Freigabe, wenn.

Gabriel
quelle
Der Beitrag wurde aktualisiert, um ihn moderner zu gestalten.
Gabriel