4 votes

Imprimer un vecteur de vecteurs dans un flux de sortie

Veuillez considérer le code suivant. J'essaie de renvoyer un vecteur de vecteurs vers un flux de sortie.

#include 
#include 
#include 
#include 
#include 

template
std::ostream &operator <<(std::ostream &os, const std::vector &v) {
    using namespace std;
    copy(v.begin(), v.end(), ostream_iterator(os, "\n"));
    return os;
}

int main() {
    using namespace std;
    vector v1;
    cout << v1;
    vector > v2;
    cout << v2;
    return 0;
}

La déclaration où j'imprime un vecteur de chaînes de caractères fonctionne. Celle où j'imprime un vecteur de vecteurs de chaînes de caractères ne fonctionne pas. J'utilise g++ 4.7.0. J'ai essayé avec ou sans le drapeau -std=c++11. En mode C++11, cela me donne cette ligne dans la demi-page d'erreurs.

error: cannot bind 'std::ostream_iterator >, char, std::char_traits >::ostream_type {aka std::basic_ostream}' lvalue to 'std::basic_ostream&&'

Je ne pense pas comprendre ce que cela veut dire. Est-ce que quelqu'un pourrait m'expliquer ? Je sais plus ou moins ce qu'est une référence rvalue, mais je ne vois pas pourquoi std::basic_ostream ne se lierait pas à std::basic_ostream&&. Peut-être que je ne le connais pas assez bien. Et y a-t-il un meilleur moyen de faire cela ?

Merci d'avance.

8voto

Crazy Eddie Points 23778

L'erreur que vous obtenez est un peu trompeuse. Lorsque j'ai essayé de compiler votre programme, j'ai dû creuser un peu dans le code du modèle, et j'ai fini par comprendre ce qui se passait:

erreur: pas de correspondance pour 'operator<<' dans '*((std::ostream_iterator >, char, std::char_traits >*)this)->std::ostream_iterator >, char, std::char_traits >::_M_stream << __value'

Essentiellement, lorsque vous avez appelé l'algorithme de copie, il a utilisé l'itérateur de sortie, qui utilise l'opérateur << à l'intérieur de l'espace de noms std. Ensuite, la recherche a dicté qu'il devait essayer de trouver une surcharge pour le modèle vector<> dans l'espace de noms std (parce que c'est là qu'IL réside).

Donc ce que vous devez faire, c'est déclarer votre opérateur de flux pour le modèle vector dans l'espace de noms std. Entourez votre code avec namespace std {} et voyez ce qui se passe...

Il convient de noter que ce que vous faites modifie essentiellement std::vector<> et ajoute un comportement qui n'était pas là auparavant. Faire cela est non standard, indéfini et peut facilement vous compliquer la tâche. Vous pourriez envisager d'autres options.


J'avais tort de penser qu'il s'agissait d'un problème de lookup de Koenig. Ce n'est pas le cas, le problème est un masquage de nom similaire à ce qui se produit dans les classes où vous déclarez une surcharge de quelque chose dans une base (et non un remplacement).

L'espace de noms standard déclare plusieurs opérateurs '<<'. Ce sont essentiellement des fonctions nommées operator <<. En essence, voici ce que vous avez:

void fun(int);

namespace Test {

  void fun() { fun(3); }

}

int main() {
    Test::fun();
}

Remarquez que vous pouvez utiliser fun(int) à partir de l'espace de noms global ou de tout espace de noms qui n'a pas de fonction nommée fun dedans. Vous ne pouvez pas l'utiliser depuis l'espace de noms Test.

C'est pourquoi votre utilisation de l'opérateur << déclaré globalement fonctionne bien à partir de l'espace de noms global, mais pas à partir de l'espace de noms std. L'espace de noms std a déjà des éléments portant le même nom que la surcharge que vous essayez de fournir, donc cette surcharge est masquée pour tous les éléments à l'intérieur de std. Si vous pouviez mettre une déclaration `using` là-bas, les choses seraient différentes.

4voto

Nawaz Points 148870

Vous avez besoin de cette bibliothèque d'utilitaires :


Si vous voulez le faire vous-même (pour vous enseigner), vous devez définir deux surcharges comme :

  • Pour std::vector:

    template
    std::ostream &operator <<(std::ostream &os, const std::vector &v) {
       using namespace std;
       copy(v.begin(), v.end(), ostream_iterator(os, "\n"));
       return os;
    }
  • Pour std::vector>:

    template
    std::ostream &operator <<(std::ostream &os, const std::vector> &v) {
       using namespace std;
    
       //NOTE: for some reason std::copy doesn't work here, so I use manual loop
       //copy(v.begin(), v.end(), ostream_iterator>(os, "\n"));
    
       for(size_t i = 0 ; i < v.size(); ++i)
            os << v[i] << "\n";
       return os;
    }

Si vous avez ces surcharges, alors elles géreront ces cas récursivement ensemble :

std::vector  v;
std::vector>  vv;
std::vector>>  vvv;
std::vector>>>  vvvv;

std::cout << v << std::endl; //ok
std::cout << vv << std::endl; //ok
std::cout << vvv << std::endl; //ok
std::cout << vvvv << std::endl; //ok

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X