2010-12-15 56 views
10

Inutile de dire plus que le code suivant:Pourquoi ostream_iterator ne fonctionne pas comme prévu?

#include <utility> 
#include <vector> 
#include <iostream> 
#include <iterator> 

using namespace std; 

typedef pair<char, char> PAIR; 

ostream& operator <<(ostream& os, const PAIR& r) 
{ 
    return os << r.first; 
} 

int main() 
{ 
    vector<PAIR> coll; 

    cout << coll[0]; // OK. 

    // The following line will cause a compilation error! Why??? 
    copy(coll.begin(), coll.end(), ostream_iterator<PAIR>(cout)); 
} 

Répondre

9

Le problème est que la recherche de nom ne trouve pas votre operator<<(ostream& os, const PAIR& r). Le code qui tente d'invoquer le operator<< se trouve quelque part à l'intérieur du ostream_iterator<> qui est lui-même à l'intérieur de l'espace de noms std. La recherche de nom recherche la bonne fonction à l'intérieur de ostream_iterator<> et l'espace de noms std; La recherche dépendant de l'argument n'aide pas ici car les deux paramètres se trouvent également dans l'espace de noms std. Donc, ma suggestion est (1) soit d'envelopper votre opérateur dans namespace std { }, mais c'est UB, IIRC. Ou (2) créez une structure héritant de std::pair pour définir un nouveau type dans votre espace de noms et utilisez l'ADL pour trouver votre operator<<().

MISE À JOUR:

Ma 3ème suggestion est d'utiliser un manipulateur personnalisé pour imprimer la paire.

Quant à ma 2ème suggestion, si vous pouvez utiliser C++ 11, héritant de std::pair devrait être facile (non testé):

struct PAIR : std::pair 
{ 
    using std::pair::pair; 
}; 

Si vous ne pouvez pas utiliser 11 C++, alors je suggère d'utiliser un manipulateur personnalisé.

+0

Pouvez-vous expliquer comment votre dernière solution fonctionne? –

+0

@ IvanZ.Siu: Voir ma mise à jour. – wilx

9

Ceci est un problème commun: en un mot, votre operator<< ne se voit pas lors de l'instanciation std::ostream_iterator.

Lors de l'instanciation, la recherche de nom tente de trouver un operator<< dans l'espace de noms std. Les candidats seront trouvés, donc aucun autre espace de noms ne sera considéré (et, en particulier, pas l'espace de noms global). Ensuite, la résolution de surcharge entre en jeu: aucune surcharge ne correspond au type d'argument, donc la compilation échoue. Notez que la recherche dépendante de l'argument n'est pas utile car std::pair se trouve également dans l'espace de noms std.

Vous avez deux solutions:

  • votre importateur operator<< dans namespace std { }, bien que vous devriez savoir que ce qui est illégal selon la norme (17.4.3.1)
  • Évitez std::copy pour cette tâche et de l'utilisation std::for_each (soit avec un foncteur «à l'ancienne» ou lambda)
+0

@icecrime, Est-ce un défaut de la norme C++? Ou y a-t-il une justification à cela? – xmllmx

+0

@xmllmx: c'est juste la façon dont fonctionnent les espaces de noms, je ne pense pas que ce soit un défaut – icecrime

+0

@icecrime, je suis clair maintenant. – xmllmx