web-dev-qa-db-fra.com

Spécialisation C++ de la fonction de modèle dans la classe de modèle

Quelle est la syntaxe C++ pour la spécialisation d'une fonction de modèle dans une classe de modèle? Par exemple, considérons que j'ai les deux classes suivantes et leur utilisation. J'aimerais pouvoir fournir des implémentations spécialisées de la méthode X :: getAThing () pour différents types. Exemple: int, std :: string, pointeur ou classe arbitraire, etc.

template <class c1> class X {
public:
   template<typename returnT> returnT getAThing(std::string param);
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

// This blows up with the error:
// error: prototype for 'int X<c1>::getAThing(std::string)' does not match any in class 'X<c1>'
template <class c1> template <typename returnT> int X<c1>::getAThing(std::string param) {
   return getIntThing(param); // Some function that crunches on param and returns an int.
}

// More specialized definitions of getAThing() for other types/classes go here...

class Y {
public:
   static std::string getName() { return "Y"; }
};

int main(int argc, char* argv[])
{
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << endl;
   cout << "An int thing: " << anIntThing << endl;
}

J'essaie de deviner la syntaxe correcte pour la spécialisation depuis au moins une heure et je ne peux rien trouver qui puisse compiler. Toute aide serait grandement appréciée!

25
Ogre Psalm33

Donc, je suis une approche différente pour répondre à votre question. Je vais commencer par quelque chose qui fait ce que vous voulez et qui fonctionne. Et ensuite, nous pourrons peut-être trouver le moyen de le permuter pour obtenir quelque chose de plus proche de ce que vous voulez vraiment:

#include <string>
#include <iostream>

int getIntThing(const ::std::string &param);

template <typename returnT>
returnT getThingFree(const ::std::string &param);

template <>
int getThingFree<int>(const ::std::string &param)
{
   return getIntThing(param);
}

// More specialized definitions of getAThing() for other types/classes
// go here...

template <class c1> class X {
public:
   template<typename returnT> returnT getAThing(std::string param);
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

// This also works, but it would be Nice if I could explicitly specialize
// this instead of having to explicitly specialize getThingFree.
template <class c1>
template <class RT>
RT X<c1>::getAThing(std::string param) {
   // Some function that crunches on param and returns an RT.
   // Gosh, wouldn't it be Nice if I didn't have to redirect through
   // this free function?
   return getThingFree<RT>(param);
}

class Y {
public:
   static std::string getName() { return "Y"; }
};

int main(int argc, char* argv[])
{
   using ::std::cout;
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << '\n';
   cout << "An int thing: " << anIntThing << '\n';
}

Voici une autre idée qui fonctionne, et qui n’est pas exactement ce que vous voulez, mais qui est plus proche. Je pense que vous y avez pensé vous-même. C'est aussi plutôt moche dans la façon dont il utilise la déduction de type.

#include <string>
#include <iostream>

template <class c1> class X;

int getIntThing(const ::std::string &param)
{
   return param.size();
}

// You can partially specialize this, but only for the class, or the
// class and return type. You cannot partially specialize this for
// just the return type. OTOH, specializations will be able to access
// private or protected members of X<c1> as this class is declared a
// friend.
template <class c1>
class friendlyGetThing {
 public:
   template <typename return_t>
   static return_t getThing(X<c1> &xthis, const ::std::string &param,
                            return_t *);
};

// This can be partially specialized on either class, return type, or
// both, but it cannot be declared a friend, so will have no access to
// private or protected members of X<c1>.
template <class c1, typename return_t>
class getThingFunctor {
 public:
   typedef return_t r_t;

   return_t operator()(X<c1> &xthis, const ::std::string &param) {
      return_t *fred = 0;
      return friendlyGetThing<c1>::getThing(xthis, param, fred);
   }
};

template <class c1> class X {
public:
   friend class friendlyGetThing<c1>;

   template<typename returnT> returnT getAThing(std::string param) {
      return getThingFunctor<c1, returnT>()(*this, param);
   }
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

class Y {
public:
   static std::string getName() { return "Y"; }
};

template <class c1>
class getThingFunctor<c1, int> {
 public:
   int operator()(X<c1> &xthis, const ::std::string &param) {
      return getIntThing(param);
   }
};

// More specialized definitions of getAThingFunctor for other types/classes
// go here...

int main(int argc, char* argv[])
{
   using ::std::cout;
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << '\n';
   cout << "An int thing: " << anIntThing << '\n';
}

Je recommanderais de déclarer getThingFunctor et friendlyGetThing dans un espace de noms semi-privé.

6
Omnifarious

D'après les informations dont je dispose (et les experts en normalisation peuvent me corriger), vous ne pouvez pas spécialiser une fonction gabarit d'un modèle de classe sans spécialiser la classe elle-même ...

je pense que cela va marcher:

template <> template <> int X<Y>::getAThing<int>(std::string param) {
   return getIntThing(param); // Some function that crunches on param and returns an int.
}
18
Nim

C++ n'a pas de concept de spécialisation partielle pour les modèles de fonction. Cependant, vous pouvez obtenir le même effet qu'une spécialisation complète en surchargeant les fonctions.

Je suppose que vous avez quelque chose comme ça, qui est vraiment l'un des seuls moyens de le faire.

template<class TYPE>
class MyInterface {
public:
    template<class RETURN>
    RETURN myFunction(RETURN& ref, ....);
};

Dans ce cas, vous spécialisez "myFunction ()" en déclarant des fonctions membres ordinaires avec le type souhaité. Les règles de surcharge de fonctions de C++ doivent vous donner ce que vous voulez, par exemple.

template<class TYPE>
class MyInterface {
public:
    template<class RETURN>
    RETURN myFunction(RETURN& ref, ....);

    // String specialization
    std::string myFunction(std::string& ref, ...);
};

Le compilateur utilisera la fonction "std :: string" le cas échéant et ne pourra jamais utiliser le modèle interne.

7
Zack Yezek

Pour les curieux, c'est probablement la solution que je vais utiliser dans mon propre code. Ceci est une légère variation de la réponse de Omnifarious , qui élimine le besoin d'une classe supplémentaire. Je donne toujours des accessoires à Omnifarious , comme il a fait la majeure partie du travail des jambes:

#include <iostream>
#include <string>

using namespace std;

// IMPORTANT NOTE: AdaptingFunctor has no access to the guts of class X!
// Thus, this solution is somewhat limited.
template<typename t1> class AdaptingFunctor {
public:
   t1 operator() (string param);
};

// Can specialize AdaptingFunctor for each type required:
template<> int AdaptingFunctor<int>::operator() (string param)
{
   return param.size(); // <=== Insert required type-specific logic here
}

// Additional specializations for each return type can go
// here, without requiring specialization of class c1 for X...


template <class c1> class X {
public:
   template<typename returnT>  returnT getAThing(std::string param)
      {
     AdaptingFunctor<returnT> adapter;
     return adapter(param);
      }
   static std::string getName();
private:
   c1 theData;
};

// Template definition of class method works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

class Y {
public:
   static std::string getName() { return "Y"; }
};


int main(int argc, char* argv[])
{
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << endl;
   cout << "An int thing: " << anIntThing << endl;
}
1
Ogre Psalm33

Essayer

template <>
template <class T>
int X<T>::template getAThing<int>(std::string param)
{
   return getIntThing(param);
}

Cela ne compile toujours pas, mais il est plus proche que vous.

Je pense que vous ne pouvez pas spécialiser les membres en masse. Avant de pouvoir spécialiser ses membres, vous devez spécifier une spécialisation particulière du modèle de classe.

0
Ben Voigt