Question

J'ai besoin d'un modèle comme celui-ci, qui fonctionne parfaitement

template <typename container> void mySuperTempalte (const container myCont)
{
    //do something here
}

alors je veux spécialiser le modèle ci-dessus pour std :: string donc je suis venu avec

template <typename container> void mySuperTempalte (const container<std::string> myCont)
{
    //check type of container
    //do something here
}

qui ne fonctionne pas et génère une erreur. Je voudrais faire fonctionner le deuxième exemple et ensuite SI possible, je voudrais ajouter du code dans le modèle pour vérifier si un std :: vector / std :: deque / std :: list a été utilisé, pour faire quelque chose différemment dans chaque Cas. J'ai donc utilisé des modèles car 99% du code est le même pour les vecteurs et les deques, etc.

Était-ce utile?

La solution

Si je comprends bien votre problème, vous disposez d'un algorithme qui fonctionnera pour les conteneurs STL vector, deque, etc., mais vous tenterez d'écrire une spécialisation de modèle pour string. Si tel est le cas, vous pouvez écrire la méthode généralisée basée sur un modèle que vous avez définie dans votre question: -

template<typename container> void mySuperTempalte( const container &myCont )
{
    // Implement STL container code
}

Ensuite, pour votre spécialisation de chaînes, vous déclarez: -

template<> void mySuperTempalte( const container<std::string> &myCont )
{
    // Implement the string code
}

Pour toute autre spécialisation, il suffit de modifier la déclaration de type pour myCont. Si vous avez vraiment besoin de faire cela pour les conteneurs vectoriels et deque, faites alors du paramètre template le paramètre du type dans ce conteneur plutôt que le conteneur lui-même comme suggéré par Sep.

template<typename C> void mySuperTempalte( const std::vector<C> &myCont)
{
    // check type of container
    // do something here
}

Cela vaut la peine d'essayer d'éviter cela en faisant en sorte que votre première implémentation fonctionne avec tous les conteneurs STL pour vous simplifier la vie. Vous n'avez alors besoin que de la spécialisation pour la classe string. Pensez même à convertir votre chaîne en un vecteur pour éviter la spécialisation dans son ensemble.

En passant, j'ai remplacé le paramètre container par une référence const. Je suppose que c'est ce que vous voulez, car vous déclarez quand même l'objet const, vous éviterez ainsi une copie.

Autres conseils

Pour spécialiser:

template<> void mySuperTempalte<std:string>(const std::string myCont)
{
    //check type of container
    //do something here
}

Pour se spécialiser sur un vecteur:

template<typename C> void mySuperTempalte (std::vector<C> myCont)
{
    //check type of container
    //do something here
}

Spécialiser pour deque:

template<typename C> void mySuperTempalte (std::deque<C> myCont)
{
    //check type of container
    //do something here
}

Avez-vous essayé un paramètre de type nom-modèle? La syntaxe est un peu étrange car elle émule la syntaxe utilisée pour déclarer un tel conteneur. Il existe un bon article InformIT qui explique cela plus en détail.

template <template <typename> class Container>
void mySuperTemplate(Container<std::string> const& cont) {
}

Notez que vous devez également déclarer l'argument comme référence!

Au fait: ce commentaire

//check type of container

est un cadeau mortel que vous faites quelque chose de mal. Vous ne voulez pas vérifier le type du conteneur. Une surcharge plus sophistiquée pour l'utilisateur, comme indiqué dans la réponse de sep.

Jusqu'à présent, les réponses semblent utiles, mais je pense que j'utiliserais un concept différent. Je m'attends à ce que tous les conteneurs définissent value_type, tout comme les conteneurs STL. Par conséquent, je peux écrire

inline template <typename C> void mySuperTemplate (C const& myCont)
{
    mySuperTemplateImpl<C, typename C::value_type>(myCont);
}

En général, il est plus facile d'agir sur un paramètre que vous avez extrait explicitement.

@sep

Solution "simple"

La réponse publiée par 'sep' est assez bonne, probablement suffisante pour 99% des développeurs d'applications, mais pourrait être améliorée si elle fait partie d'une interface de bibliothèque, pour répéter:

  

Pour se spécialiser sur un vecteur:

template<typename C> void mySuperTempalte (std::vector<C> myCont)
{
    //check type of container
    //do something here
}

Cela fonctionnera si l'appelant n'utilise pas std :: vector. Si cela fonctionne assez bien pour vous, pour vous spécialiser en vecteur, liste, etc., alors arrêtez-vous ici et utilisez-le.

Solution plus complète

Tout d'abord, notez que vous ne pouvez pas spécialiser partiellement les modèles de fonction. Vous pouvez créer des surcharges. Et si deux ou plusieurs d'entre eux correspondent au même degré, vous obtiendrez une "surcharge ambiguë". les erreurs. Nous devons donc faire correspondre exactement un match dans chaque cas que vous souhaitez soutenir.

Une technique pour cela consiste à utiliser la technique enable_if - enable_if vous permet de supprimer de manière sélective les surcharges de modèles de fonction de la liste de correspondance possible à l'aide d'une règle de langage obscure ... en gros, si une expression booléenne est fausse, la surcharge devient 'invisible'. Recherchez SFINAE pour plus d'informations si vous êtes curieux.

Exemple. Ce code peut être compilé à partir de la ligne de commande avec MinGW (g ++ parameterize.cpp) ou VC9 (cl / EHsc parameterize.cpp) sans erreur:

#include <iostream>
#include <vector>
#include <string>
using namespace std;

template <bool B, class T> struct enable_if {};
template <class T> struct enable_if<true, T> { typedef T type; };

template <class T, class U> struct is_same { enum { value = false }; };
template <class T> struct is_same<T,T> { enum { value = true }; };

namespace detail{
    // our special function, not for strings
    //   use ... to make it the least-prefered overload
    template <class Container>
    void SpecialFunction_(const Container& c, ...){
        cout << "invoked SpecialFunction() default\n";
    }

    // our special function, first overload:
    template <class Container>
    // enable only if it is a container of mutable strings
    typename enable_if<
        is_same<typename Container::value_type, string>::value, 
        void
    >::type
    SpecialFunction_(const Container& c, void*){
        cout << "invoked SpecialFunction() for strings\n";
    }
}

// wrapper function
template <class Container>
void SpecialFunction(const Container& c){
    detail::SpecialFunction_(c, 0);
}

int main(){
    vector<int> vi;
    cout << "calling with vector<int>\n";
    SpecialFunction(vi);

    vector<string> vs;
    cout << "\ncalling with vector<string>\n";
    SpecialFunction(vs);
}

Sortie:

d:\scratch>parameterize.exe calling
with vector<int> invoked
SpecialFunction() default

calling with vector<string> invoked
SpecialFunction() for strings

d:\scratch>

Que ce soit un bon design ou non, reste à la discussion. Quoi qu'il en soit, vous pouvez détecter le type de conteneur à l'aide de spécialisations de modèles partielles. En particulier:

enum container_types
{
   unknown,
   list_container,
   vector_container
};

template <typename T>
struct detect_container_
{
   enum { type = unknown };
};

template <typename V>
struct detect_container_< std::vector<V> > // specialization
{
   enum { type = vector_container };
};

template <typename V>
struct detect_container_< std::list<V> >
{
   enum { type = list_container };
};

// Helper function to ease usage
template <typename T>
container_types detect_container( T const & )
{
   return static_cast<container_types>( detect_container_<T>::type );
}

int main()
{
   std::vector<int> v;

   assert( detect_container( v ) == vector_container );
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top