Pregunta

Tengo el siguiente código:

#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;
}

En este código solo quiero copiar el mapa al flujo de salida. Para hacer esto, necesito definir el operador & Lt; & Lt; (..) - OK. Pero según los nombres, el compilador de reglas de búsqueda no puede encontrar mi operador & Lt; & Lt; ().
Porque std :: cout, std :: pair y std :: copy que llamaron a mi operador & Lt; & Lt; - todo desde el espacio de nombres estándar.

Solución rápida: agregue mi orador < < al espacio de nombres estándar, pero es feo, en mi humilde opinión.

¿Qué soluciones o soluciones para este problema conoce?

¿Fue útil?

Solución 2

He fundado una nueva forma elegante de resolver este problema.
Tengo muchas ideas de interés cuando leo las respuestas:

  • iterador de ajuste, para transformar std :: pair en std :: string;
  • wrap std :: pair, para tener la oportunidad de sobrecargar al operador < < (...);
  • use std :: for_each habitual con el functor de impresión;
  • use std :: for_each con boost :: labda - se ve bien, excepto acceder a std :: pair < > :: first y std :: pair < > :: segundos miembros;

Creo que utilizaré todas estas ideas en el futuro para resolver otros problemas diferentes.
Pero para este caso, he entendido que puedo formular mi problema b como & Quot; transformar los datos del mapa en cadenas y escribirlos en la secuencia de salida & Quot; en su lugar " copie los datos del mapa a la secuencia de salida " ;. Mi solución se ve así:

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 );

Creo que este método es más corto y expresivo que otros.

Otros consejos

No hay una forma estándar de crear un std::pair porque, bueno, cómo quieres que se imprima probablemente sea diferente de la forma en que lo quiere el siguiente. Este es un buen caso de uso para un functor personalizado o una función lambda. Luego puede pasarlo como argumento a std::for_each para hacer el trabajo.

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";
    }
}

Para llamar a este functor desde su código:

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

Me gustaría señalar que agregar cosas al espacio de nombres std :: es ilegal de acuerdo con el Estándar C ++ (ver sección 17.4.3.1).

Lo que quieres es un iterador transformador. Este tipo de iterador envuelve a otro iterador, reenvía todos los métodos de posicionamiento como operator ++ y operator ==, pero redefine operator * y operator - & Gt ;.

Boceto rápido:

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 + "]";
    }
...

Simplemente de paso, pero esto hizo el trabajo para mí, así que puede hacerlo para otra persona (versión cortada):

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

Caso de uso dado:

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

Usando Boost Lambda, puedes probar algo como esto. La versión que tengo de Boost Lambda, esto en realidad no funciona, probaré y arreglaré más tarde.

#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 ) );

[Prefiero eliminar esta respuesta, pero la dejaré por ahora, en caso de que alguien encuentre interesante la discusión.]

Dado que es una extensión razonable de la biblioteca estándar, simplemente lo pondría en el espacio de nombres estándar, especialmente si se trata de una sola vez. Puede declararlo como estático para evitar que provoque errores de enlazador, en caso de que otra persona haga lo mismo en otro lugar.

Otra solución que viene a la mente es crear un contenedor para 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;

más simple y universal!

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

}); 

--- Está bien con C ++ 11

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top