std :: copy в std :: cout для std :: pair - PullRequest
16 голосов
/ 11 марта 2009

У меня есть следующий код:

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

В этом коде я просто хочу скопировать карту в выходной поток. Для этого мне нужно определить оператор << (..) - ОК. Но по правилам поиска имен компилятор не может найти мой оператор << (). <br> Потому что std :: cout, std :: pair и std :: copy, которые вызвали мой оператор << - все из пространства имен std. </p>

Быстрое решение - добавить мой oerator << в пространство имен std - но это ужасно, imho. </p>

Какие решения или обходные пути для этой проблемы вы знаете?

Ответы [ 9 ]

16 голосов
/ 11 марта 2009

Не существует стандартного способа присмотреть за std::pair, потому что то, как вы хотите его напечатать, вероятно, отличается от того, как этого хочет следующий парень. Это хороший пример использования пользовательского функтора или лямбда-функции. Затем вы можете передать это в качестве аргумента std::for_each для выполнения работы.

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

Для вызова этого функтора из вашего кода:

std::for_each(some_map.begin(),
              some_map.end(),
              PrintMyMap<MyMap::value_type>(std::cout));
14 голосов
/ 14 марта 2009

Я нашел один новый элегантный способ решения этой проблемы.
У меня появляется много интересных идей, когда читаешь ответы:

  • Итератор переноса, для преобразования std :: pair в std :: string;
  • wrap std :: pair, для возможности перегрузить оператор << (...); <br>
  • использовать обычный std :: for_each с функтором печати;
  • использовать std :: for_each с boost :: labda - выглядит красиво, за исключением доступа к членам std :: pair <> :: first и std :: pair <> :: second;

Я думаю, что буду использовать все эти идеи в будущем для решения других проблем.
Но для этого случая я понял, что могу сформулировать свою проблему b как «преобразовать данные карты в строки и записать их в выходной поток» вместо «скопировать данные карты в выходной поток». Мое решение выглядит так:

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

Я думаю, что этот метод самый короткий и выразительный, чем другие.

10 голосов
/ 11 марта 2009

Я просто хотел бы отметить, что добавление вещей в пространство имен std :: является недопустимым согласно стандарту C ++ (см. Раздел 17.4.3.1).

5 голосов
/ 11 марта 2009

То, что вы хотите, это итератор преобразования. Этот тип итератора оборачивает другой итератор, перенаправляя все методы позиционирования, такие как operator ++ и operator ==, но переопределяет operator * и operator ->.

Быстрый набросок:

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 + "]";
    }
...
4 голосов
/ 21 октября 2012

Просто проходя мимо, но это сделало работу для меня, так что это может для кого-то еще (сокращенная версия):

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

Указан вариант использования:

transform (v.begin (), v.end (), 
           ostream_iterator<int>(cout, "\n"), first_of<int, string> ());
2 голосов
/ 11 марта 2009

Используя Boost Lambda, вы можете попробовать что-то вроде этого. Версия Boost Lambda, которая у меня есть, на самом деле не работает, позже я проверю и исправлю

#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 ) );
2 голосов
/ 11 марта 2009

[Я бы предпочел удалить этот ответ, но пока оставлю его на случай, если кому-то будет интересно обсуждение.]

Поскольку это разумное расширение библиотеки std, я бы просто поместил ее в пространство имен std, особенно если это одноразовая вещь. Вы можете просто объявить его статическим, чтобы он не вызывал ошибок компоновщика, если кто-то еще сделает то же самое в другом месте.

Другое решение, которое приходит на ум, - это создание оболочки для 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) { ... }
1 голос
/ 18 марта 2016
    for (const auto& your_pair : your_container)
        your_stream << "[" << your_pair.first << "," << your_pair.second << "]" << endl;

более простой и универсальный!

0 голосов
/ 14 декабря 2014
 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 ++ 11

...