Question

J'ai le code suivant:

#include <iostream>
#include <algorithm>
#include <map>
#include <iterator>

//namespace std
//{

std::ostream& operator << ( std::ostream& out, 
                const std::pair< size_t, size_t >& rhs )
{
    out << rhs.first << ", " << rhs.second;
    return out;
}
//}

int main() 
{

    std::map < size_t, size_t > some_map;

    // fill  some_map with random values
    for ( size_t i = 0; i < 10; ++i )
    {
        some_map[ rand() % 10 ] = rand() % 100;
    }

    // now I want to output this map
    std::copy( 
        some_map.begin(), 
        some_map.end(), 
        std::ostream_iterator< 
              std::pair< size_t, size_t > >( std::cout, "\n" ) );

    return 0;
}

Dans ce code, je veux juste copier la carte dans le flux de sortie. Pour ce faire, j'ai besoin de définir l'opérateur & Lt; & Lt; (..) - OK. Mais selon les noms, le compilateur ne trouve pas mon opérateur & Lt; & Lt; ().
Parce que std :: cout, std :: pair et std :: copy ont appelé mon opérateur & Lt; & Lt; - tout de l'espace de noms std.

Solution rapide - ajouter mon opérateur < < to std namespace - mais c'est moche, à mon humble avis.

Quelles solutions ou solutions de contournement pour ce problème connaissez-vous?

Était-ce utile?

La solution 2

J'ai mis au point un nouveau moyen élégant de résoudre ce problème.
J'ai de nombreuses idées d'intérêt lorsque je lis les réponses:

  • Wrap Iterator, pour transformer std :: pair en std :: string;
  • wrap std :: pair, pour avoir une chance de surcharger l'opérateur < < (...);
  • utilise std :: for_each avec le foncteur d’impression;
  • utiliser std :: for_each avec boost :: labda - a l’air sympa, sauf d’avoir accès à std :: pair < > :: first et std :: pair < > :: second members;

Je pense que je vais utiliser toutes ces idées à l'avenir pour résoudre différents autres problèmes.
Mais dans ce cas, j'ai cru comprendre que je pouvais formuler mon problème comme & "Transformer les données de la carte en chaînes et les écrire dans le flux de sortie &"; au lieu de cela " copier les données de la carte dans le flux de sortie " ;. Ma solution ressemble à:

namespace
{
std::string toString( const std::pair< size_t, size_t >& data)
{
    std::ostringstream str;
    str << data.first << ", " << data.second;
    return str.str();
}
} // namespace anonymous

std::transform( 
    some_map.begin(), 
    some_map.end(), 
    std::ostream_iterator< std::string >( std::cout, "\n" ),
    toString );

Je pense que cette méthode est plus courte et expressive que d’autres.

Autres conseils

Il n’existe pas de méthode standard pour comptabiliser un std::pair parce que la façon dont vous voulez l’imprimer est probablement différente de celle que le prochain type veut. C'est un bon cas d'utilisation pour un foncteur personnalisé ou une fonction lambda. Vous pouvez ensuite passer cela comme argument à std::for_each effectuer le travail.

typedef std::map<size_t, size_t> MyMap;

template <class T>
struct PrintMyMap : public std::unary_function<T, void>
{
    std::ostream& os;
    PrintMyMap(std::ostream& strm) : os(strm) {}

    void operator()(const T& elem) const
    {
        os << elem.first << ", " << elem.second << "\n";
    }
}

Pour appeler ce foncteur depuis votre code:

std::for_each(some_map.begin(),
              some_map.end(),
              PrintMyMap<MyMap::value_type>(std::cout));

Je voudrais juste souligner que l'ajout d'éléments à std :: namespace est illégal selon la norme C ++ (voir la section 17.4.3.1).

Ce que vous voulez, c'est un itérateur en transformation. Ce type d'itérateur englobe un autre itérateur, transmet toutes les méthodes de positionnement telles que opérateur ++ et opérateur ==, mais redéfinit l'opérateur * et l'opérateur - & Gt;.

Esquisse rapide:

template <typename ITER> 
struct transformingIterator : private ITER {
    transformingIterator(ITER const& base) : ITER(base) {}
    transformingIterator& operator++() { ITER::operator++(); return *this; }
    std::string operator*() const
    {
        ITER::value_type const& v = ITER::operator*();
        return "[" + v->first +", " + v->second + "]";
    }
...

Juste en passant, mais cela a fonctionné pour moi, donc pour quelqu'un d'autre (version coupée):

template<typename First, typename Second>
struct first_of {
    First& operator()(std::pair<First, Second>& v) const {
        return v.first;
    }
};

Cas d'utilisation donné:

transform (v.begin (), v.end (), 
           ostream_iterator<int>(cout, "\n"), first_of<int, string> ());

En utilisant Boost Lambda, vous pouvez essayer quelque chose comme ceci. La version que j'ai de Boost Lambda, cela ne fonctionne pas vraiment, je vais tester et réparer plus tard.

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;

std::for_each( some_map.begin(), some_map.end(), 
               std::cout << bind( &std::map<size_t,size_t>::value_type::first, _1 )
                         << ","
                         << bind( &std::map<size_t,size_t>::value_type::second, _1 ) );

[Je préfère supprimer cette réponse, mais je la laisse pour le moment, au cas où quelqu'un trouverait la discussion intéressante.]

Puisqu'il s'agit d'une extension raisonnable de la bibliothèque std, je le mettrais simplement dans un espace de noms std, surtout s'il s'agit d'une opération ponctuelle. Vous pouvez simplement le déclarer statique pour l'empêcher de causer des erreurs de l'éditeur de liens, si quelqu'un d'autre faisait la même chose ailleurs.

Une autre solution qui me vient à l’esprit est de créer un wrapper pour std :: pair:

template<class A, class B>
struct pairWrapper {
  const std::pair<A,B> & x;
  pairWrapper(const std::pair<A,B> & x) : x(x) {}
}

template<class A,class B>
std::ostream & operator<<(std::ostream & stream, const pairWrapper<A,B> & pw) { ... }
    for (const auto& your_pair : your_container)
        your_stream << "[" << your_pair.first << "," << your_pair.second << "]" << endl;

plus simple et universel!

 for_each(some_map.begin(), some_map.end(), [](const std::map < size_t, size_t >::value_type &ite){
             cout<<ite.first<<" "<<ite.second<<endl;

}); 

--- C'est bon avec C ++ 11

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top