2011-09-26 3 views
5

Comment écrire une fonction modèle qui fonctionne sur un conteneur arbitraire de type arbitraire? Par exemple, comment puis-je généralise cette fonction facticeModèle de modèle Fonction C++

template <typename Element> 
void print_size(const std::vector<Element> & a) 
{ 
    cout << a.size() << endl; 
} 

à

template <template<typename> class Container, typename Element> 
void print_size(const Container<Element> & a) 
{ 
    cout << a.size() << endl; 
} 

Voici une utilisation typique

std::vector<std::string> f; 
print_size(f) 

Cette erreur donne

tests/t_distances.cpp:110:12: error: no matching function for call to ‘print(std::vector<std::basic_string<char> >&)’. I'm guessing I must tell the compiler something more specific about what types that are allowed. 

Quelle est cette variante de l'utilisation de modèle appelé et comment puis-je résoudre ce problème?

Répondre

13

Existe-t-il une raison spécifique pour utiliser un modèle de modèle? Pourquoi pas comme ça?

template <typename Container> 
void print_size(Container const& a) 
{ 
    cout << a.size() << endl; 
} 

En général, les modèles de modèle ne valent pas le problème. Dans votre cas particulier, il n'y a certainement aucune utilité pour eux, et si vous avez vraiment besoin d'accéder au type de membre, je vous suggère de vous incliner devant la pratique courante et d'utiliser une métafonction (typename Container::value_type dans ce cas).

+0

Et si vous avez besoin du type "contenu"? Comme si vous vouliez créer une fonction qui vous donne une std :: map de deux std :: vector (une pour les clés, l'autre pour les valeurs). Mais vous pouvez utiliser autre chose que std :: vector comme une classe personnalisée avec les itérateurs begin() et end(). – ibizaman

+0

@ibizaman Voilà ce à quoi servent les typendefs. Utilisez simplement 'typename Container :: value_type' (notez le préfixe' typename', c'est important, il y a d'autres questions qui expliquent pourquoi). –

+0

est ce standard value_type? Eh bien, je sais que c'est dans la stl pour std :: vector et ainsi de suite, mais est-ce la façon reconnue de le faire pour les classes personnalisées? – ibizaman

1

Pourquoi jamais utiliser quelque chose comme

template <template<typename> class Container, typename Element> 
void print_size(const Container<Element> & a) 
{ 
    cout << a.size() << endl; 
} 

? Utilisez de manière plus simple:

template<typename Container> 
void print_size(const Container & a) 
{ 
    cout << a.size() << endl; 
} 

Lorsque vous appelez print_size(f), vous appellerez print_size avec Container étant vector<string>.

3

Le problème est que le modèle vector prend deux arguments de type, et votre modèle accepte uniquement les arguments de modèle qui acceptent un seul argument. La solution la plus simple est levée un peu de sécurité de type et en utilisant simplement Container comme type:

template <typename Container> 
void print_size(Container const & c) { 
    std::cout << c.size() << std::endl; 
} 

Peut-être ajouter des contrôles statiques (quel que soit le type Container est, il doit avoir un type imbriqué value_type qui est Element ... L'alternative serait de faire correspondre l'argument du template template aux templates qui doivent être passés (y compris allocator pour les séquences, allocator et predicate d'ordre pour les conteneurs associatifs) ...

0

Pour ces types de génériques algorithmes, I préfèrent toujours les itérateurs

template <typename IteratorType> 
void print_size(IteratorType begin, IteratorType end) { 
    std::cout << std::distance(begin, end) << std::endl; 
} 

Pour appeler:

std::vector<std::string> f; 
print_size(f.begin(), f.end()); 
+0

Pourquoi préféreriez-vous cela, en particulier en voyant que la distance peut avoir une complexité algorithmique plus grande que la fonction membre size? – UncleBens

+0

@UncleBens - oui - c'est vrai pour les itérateurs à accès non aléatoire, dans ce cas - il ne devrait pas y avoir de différence. Quoi qu'il en soit, cet exemple est trivial, ce que je veux dire c'est que (tous) les algorithmes de bibliothèque les plus courants fonctionnent avec les itérateurs, alors pourquoi ne pas faire vos algorithmes de la même manière? – Nim

2

Je sais que la question a été posée il y a longtemps, mais j'avais même problème, et la solution est que vous devez écrire

template <template <typename,typename> class Container, typename element, typename Allocator> 
void print_size(Container<element, Allocator> & a) 
{ 
    std::cout << a.size() << std::endl; 
} 

car le vecteur a deux paramètres de gabarit