2009-04-15 11 views
3

J'ai un problème étrange avec les modèles. Lorsque vous essayez de passer un itérateur paramétré, il se plaint qu'aucune fonction ne peut être trouvée. L'extrait de code est ici, oublier la fonctionnalité, il utilise la référence à l'itérateur sans canevas ce qui me intéresseComment passez-vous une référence lorsque vous utilisez un nom de type comme argument de fonction en C++?

#include <list> 
#include <iostream> 

template<typename T> 
void print_list_element(typename std::list<T>::iterator& it){ 
    std::cout << *it << std::endl; 
} 

int main() { 
    std::list<int> theList; 
    theList.push_back(1); 

    std::list<int>::iterator it = theList.begin(); 
    print_list_element(it); 

    return 0; 
} 

Si vous essayez de compiler ce avec g ++ v4.3.2, il se plaint d'un message disant que:

main.cpp:14: error: no matching function for call to 'print_list_element(std::_List_iterator<int>&)' 

Y at-il un problème avec le code que j'ai écrit ou est-ce que g ++ a besoin de plus d'informations?

Répondre

9

g ++ ne peut pas déterminer quelle surcharge de modèle print_list_element doit être utilisée. Si vous spécifiez explicitement le paramètre de modèle, il fonctionne:

print_list_element<int>(it); 
4

C'est illégal parce que std :: Liste < T> :: iterator est pas ce que la norme appelle un contexte approprié

déduit
+0

Je pense que la partie appropriée de la norme '03 est: 14.8.2.4/4. La norme dit aussi que c'est un «contexte non déduit», plutôt que votre formulation ci-dessus. –

4

Les autres réponses sont correctes , mais pour l'exhaustivité, j'ajouterai simplement que, par conception, C++ ne peut déduire automatiquement les arguments de template dans certains cas, et ceci n'en fait pas partie.

Quand vous y pensez, vous vous rendrez compte que la déduction automatique dans ce cas conduirait à une situation indésirable. std::list<T>::iterator n'est pas un vrai type, c'est juste un typedef alias pour un type réel (par exemple il peut être T*) auquel il est immédiatement traduit, donc le compilateur devrait construire une sorte d '"index inversé" pour mapper T* à std::list<T>::iterator pour la déduction automatique de T de travailler ici. Mais ce mappage se casse dès qu'un autre modèle de classe a été créé avec un membre de type appelé iterator qui a été typedef édité à T* - alors le compilateur aurait deux choix de ce à traduire T*, et aucun moyen de choisir entre eux. De toute évidence, toute politique de déduction automatique qui casse lorsque une classe non liée ajoute un membre de type typedef est beaucoup trop fragile pour fonctionner.

5

Une meilleure façon est d'écrire la fonction comme ceci:

template<typename Iter> 
void print_element(Iter it){ 
    std::cout << *it << std::endl; 
} 

Cela va maintenant travailler pour tout type de iterator, non seulement std::list<T>::iterator. En outre, le type de modèle sera déduit correctement de l'argument.

Je me rends compte que c'était un exemple artificiel, mais presque toujours vous ne vouliez probablement pas passer de list<T>::iterator à une fonction de toute façon. Au pire, au moins gabarit sur ListType pour que votre code fonctionne avec des listes avec des allocateurs personnalisés.

+0

+1. Cela fonctionnera même pour les tableaux simples. Si, pour une raison quelconque, vous voulez vraiment que cela fonctionne * seulement * pour std :: list , recherchez le template enable_if <> de Boost. –

Questions connexes