[ACCEPTED]-std::copy to std::cout for std::pair-operator-overloading

Accepted answer
Score: 16

There is no standard way to cout a std::pair because, well, how 6 you want it printed is probably different 5 from the way the next guy wants it. This 4 is a good use case for a custom functor 3 or a lambda function. You can then pass 2 that as an argument to std::for_each to do the work.

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

To 1 call this functor from your code:

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

I've founded one new elegant way to solve 10 this problem.
I've got many interest ideas 9 when read answers:

  • wrap iterator, for transform std::pair to std::string;
  • wrap std::pair, for have a chance to overload operator<<(...);
  • use usual std::for_each with printing functor;
  • use std::for_each with boost::labda - looks nice, except accessing to std::pair< >::first and std::pair< >::second members;

I think I will use all 8 of this ideas in future for solve different 7 other problems.
But for this case I've understaded 6 that I can formulate my bproblem as "transform 5 map's data to strings and write them to 4 output stream" instead "copy map's data 3 to ouput stream". My solution looks like:

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

I 2 think this method is most short and expressive 1 than others.

Score: 11

I'd just like to point out that adding things 2 to the std:: namespace is illegal according 1 to the C++ Standard (see section 17.4.3.1).

Score: 5

What you want is a transforming iterator. This 4 kind of iterator wraps another iterator, forwards 3 all positioning methods like operator++ and 2 operator==, but redefines operator* and 1 operator->.

Quick sketch :

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

Just passing by, but this did the job for 2 me, so it can for somebody else (cut version):

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

Use 1 case given:

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

[I'd rather delete this answer, but I'll 4 leave it for now, in case someone finds 3 the discussion interesting.]

Since it's a reasonable extension to the std library, I'd just put it in std namespace, especially if this is a one time thing. You can just declare it static to prevent it from causing linker errors, should someone else do the same thing someplace else.

Another solution 2 that comes to mind is to create a wrapper 1 for 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) { ... }
Score: 2

Using Boost Lambda, you could try something 3 like this. The version I have of Boost Lambda, this 2 doesn't actually work, I'll test and fix 1 later.

#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 ) );
Score: 1
    for (const auto& your_pair : your_container)
        your_stream << "[" << your_pair.first << "," << your_pair.second << "]" << endl;

more simple and universal !

0

Score: 1

Here is an adaptor to use with std::copy and std::ostream_iterator for 4 the std::pair type. You end up holding one extra 3 reference, but the compiler optimization 2 may take care of it. BTW, the first type 1 in std::pair of the std::map::value_type will be a const.

template <typename pair_type>
class pair_adaptor
{
public:
    const pair_type &m;
    pair_adaptor(const pair_type &a) : m(a) {}

    friend std::ostream &operator << (std::ostream &out, 
        const pair_adaptor <pair_type> &d)
    {
        const pair_type &m = d.m;
        return out << m.first << " => " << m.second;
    }
};

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

std::copy (mymap.begin(), mymap.end(),
    std::ostream_iterator < 
        pair_adaptor <value_type> > (std::cout, "\n"));

std::copy (mymap.begin(), mymap.end(),
    std::ostream_iterator < 
        pair_adaptor <
            std::pair<const size_t, size_t>>> (std::cout, "\n"));
Score: 0
 for_each(some_map.begin(), some_map.end(), [](const std::map < size_t, size_t >::value_type &ite){
             cout<<ite.first<<" "<<ite.second<<endl;

}); 

--- It is fine with C++11

0

More Related questions