2009-03-11 5 views
16

У меня есть следующий код:станд :: копию StD :: COUT для станд :: пары

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

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

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

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

+0

BTW, существует алгоритм STL, который выполнит первый цикл for-loop, http://www.sgi.com/tech/stl/generate.html, а затем, если вы действительно чувствуете, что случайным значениям нужны назначенные случайные местоположения , http://www.sgi.com/tech/stl/random_shuffle.html. – paxos1977

ответ

13

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

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

Я думаю, что буду использовать все эти идеи в будущем для решения других других проблем.
Но для этого случая я понял, что могу сформулировать свой 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); 

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

+0

Решение Майкла намеренно избегает toString, поэтому оно не конфликтует с другими определениями toString. Кроме того, в нескольких случаях его конец короче вашего (преобразование принимает дополнительный параметр). Таким образом, ваше утверждение о том, чтобы быть короче и выразительнее, чем лучший ответ здесь, ошибочно. – codetaku

16

Нет стандартного способа для cout 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)); 
2

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

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

Еще одно решение, которое приходит на ум, чтобы создать оболочку для станд :: пары:

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) { ... } 
+0

+1, очень приятно - конвертирующий конструктор автоматически преобразует пару в паре при необходимости. Но, пожалуйста, добавьте параметр ostream & parameter в ваш шаблонный оператор <<(). –

+0

Ницца, спасибо. – bayda

+0

Неверно, запрещено. Пространство имен std предназначено для классов, шаблонов и функций, предоставляемых компилятором. Вы не можете добавлять перегрузки. – MSalters

10

Я просто хотел бы отметить, что добавление элементов в станд :: пространства имен является незаконным в соответствии с Стандарт C++ (см. раздел 17.4.3.1).

+0

Важное замечание, спасибо. – bayda

+0

Одно исключение: вы можете добавить перегрузки для 'std :: swap'. –

+0

@konrad go у вас есть ссылка на это? – 2009-03-11 12:37:14

5

Что вы хотите, это преобразующий итератор. Этот тип итератора обертывает другой итератор, пересылает все методы позиционирования, такие как 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 + "]"; 
    } 
... 
+0

Спасибо. Хорошая идея создания оболочек итератора, эта идея может быть обобщена и использована для решения других проблем. – bayda

+0

Если вы хотите что-то общее, то один очевидный шаг - сохранить преобразование в соответствующей функции boost ::. d требуется дополнительный параметр шаблона для нового значения_type, конечно. – MSalters

2

Использование Повысьте 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)); 
4

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

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>()); 
0
for_each(some_map.begin(), some_map.end(), [](std::map < size_t, size_t >::value_type &ite){ 
      cout<<ite.first<<" "<<ite.second<<endl; 

}); 

- - Хорошо с C++ 11

1
for (const auto& your_pair : your_container) 
     your_stream << "[" << your_pair.first << "," << your_pair.second << "]" << endl; 

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

Смежные вопросы