Std:: copy to std:: cout для std:: pair
У меня есть следующий код:
#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;
}
В этом коде мне просто нужно скопировать карту в выходной поток. Для этого мне нужно определить оператор < (0) - ОК.
Но в соответствии с именами, определяющими правила, компилятор не может найти мой оператор < <().
Поскольку std:: cout, std:: pair и std:: copy, который вызывал мой оператор < < - все из пространства имен std.
Быстрое решение - добавьте мой oerator < < к пространству имен std - но это уродливо, imho.
Какие решения или обходные пути для этой проблемы вы знаете?
Ответы
Ответ 1
Я основал один новый элегантный способ решить эту проблему.
У меня есть много интересных идей, когда читайте ответы:
- wrap iterator, для преобразования std:: pair в std::string;
- wrap std:: pair, для которого есть возможность перегрузить оператор < (lt;) (...);
- используйте обычный std:: for_each с функтором печати,
- использовать std:: for_each с boost:: labda - выглядит хорошо, за исключением доступа к std:: pair < > :: first и std:: pair < > :: второй участник,
Я думаю, что буду использовать все эти идеи в будущем для решения других других проблем.
Но для этого случая я недооценил, что я могу сформулировать свой bproblem как "преобразовать данные карты в строки и записать их в поток вывода" вместо "скопировать данные карты в выходной поток". Мое решение выглядит так:
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 );
Я думаю, что этот метод наиболее короткий и выразительный, чем другие.
Ответ 2
Нет стандартного способа cout a 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));
Ответ 3
Я хотел бы отметить, что добавление объектов в пространство std:: namespace является незаконным в соответствии со стандартом С++ (см. раздел 17.4.3.1).
Ответ 4
То, что вы хотите, - это преобразующий итератор. Этот тип итератора обертывает другой итератор, пересылает все методы позиционирования, такие как 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 + "]";
}
...
Ответ 5
Просто проходил мимо, но это работало для меня, поэтому он может для кого-то другого (вырезанная версия):
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> ());
Ответ 6
[Я предпочел бы удалить этот ответ, но сейчас оставлю его, если кто-то найдет интересное обсуждение.]
Поскольку это разумное расширение для библиотеки 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) { ... }
Ответ 7
Используя 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 ) );
Ответ 8
for (const auto& your_pair : your_container)
your_stream << "[" << your_pair.first << "," << your_pair.second << "]" << endl;
более простой и универсальный!
Ответ 9
for_each(some_map.begin(), some_map.end(), [](std::map < size_t, size_t >::value_type &ite){
cout<<ite.first<<" "<<ite.second<<endl;
});
--- Это хорошо с С++ 11