2009-03-11 19 views
16

Ho codice seguente:std :: copia a std :: cout per std :: coppia

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

In questo codice voglio solo copiare mappa per flusso di output. Per fare questo ho bisogno di definire l'operatore < < (..) - OK. Ma secondo nomi che trovano regole il compilatore non trova il mio operatore < <().
Perché std :: cout, std :: pair e std :: copy che ha chiamato il mio operatore < < - tutto dallo spazio dei nomi std.

Soluzione rapida: aggiungi il mio oerator < < allo spazio dei nomi std, ma è brutto, imho.

Quali soluzioni o soluzioni temporanee per questo problema conosci?

+0

BTW, esiste un algoritmo STL che eseguirà il primo ciclo for, http://www.sgi.com/tech/stl/generate.html, quindi se si ritiene veramente che i valori casuali debbano essere assegnati a posizioni casuali , http://www.sgi.com/tech/stl/random_shuffle.html. – paxos1977

risposta

13

Ho trovato un nuovo modo elegante per risolvere questo problema.
Ho ottenuto molte idee di interesse quando leggere le risposte:

  • involucro iteratore, per trasformare std :: coppia a std :: string;
  • wrap std :: coppia, per avere la possibilità di sovraccaricare l'operatore < < (...);
  • utilizzare usualmente std :: for_each con il functor di stampa;
  • utilizzare std :: for_each con boost :: labda - sembra bello, tranne l'accesso a std :: coppia <> :: first e std :: pair <> :: second members;

Penso che userò tutte queste idee in futuro per risolvere diversi altri problemi.
Ma in questo caso ho verificato che posso formulare il mio bproblem come "trasformare i dati della mappa in stringhe e scriverli nello stream di output" invece "copiare i dati della mappa nello stream di output". La mia soluzione si presenta come:

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

Penso che questo metodo è più breve ed espressivo di altri.

+0

La soluzione di Michael evita intenzionalmente toString in modo che non sia in conflitto con altre definizioni di toString.Inoltre, su più usi, il suo finisce essere più corto del tuo (la trasformazione richiede un parametro aggiuntivo Quindi la tua pretesa di essere più breve e più espressiva della migliore risposta qui è sbagliata. – codetaku

16

Non esiste un modo standard per coutire un std::pair perché, beh, come lo si vuole stampare è probabilmente diverso dal modo in cui il prossimo lo vuole. Questo è un buon caso d'uso per un functor personalizzato o una funzione lambda. È quindi possibile passarlo come argomento a std::for_each per eseguire il lavoro.

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

di chiamare questo funtore dal codice:

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

[preferirei eliminare questa risposta, ma lascio per ora, nel caso in cui qualcuno trova la discussione interessante.]

Poiché è un'estensione ragionevole della libreria std, lo inserisco semplicemente nello spazio dei nomi std, specialmente se si tratta di una cosa singola. Puoi semplicemente dichiararlo statico per evitare che causi errori di linker, se qualcun altro fa la stessa cosa da qualche altra parte.

Un'altra soluzione che viene in mente è quello di creare un wrapper per std :: coppia:

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, molto bello - il costruttore di conversione convertirà automaticamente una coppia in un wrapper quando necessario. Ma per favore aggiungi l'ostream e il parametro all'operatore di modello <<(). –

+0

Bello, grazie. – bayda

+0

Errato, non consentito. Namespace std è per classi, template e funzioni forniti dal compilatore. Non è possibile aggiungere sovraccarichi. – MSalters

10

vorrei solo far notare che l'aggiunta di cose da std :: namespace è illegale in base alle lo standard C++ (vedere la sezione 17.4.3.1).

+0

Nota importante, grazie. – bayda

+0

Un'eccezione: puoi aggiungere sovraccarichi per 'std :: swap'. –

+0

@konrad, hai un riferimento per questo? –

5

Quello che vuoi è un iteratore di trasformazione. Questo tipo di iteratore avvolge un altro iteratore, inoltra tutti i metodi di posizionamento come operator ++ e operator ==, ma ridefinisce operatore * e operatore->.

rapido schizzo:

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

Grazie. Bella idea per creare wrapper iteratori, questa idea potrebbe essere generalizzata e utilizzata per risolvere altri problemi – bayda

+0

Se vuoi qualcosa di generico, un passaggio ovvio è memorizzare la trasformazione in una funzione boost :: appropriata. D ha bisogno di un parametro template aggiuntivo per il nuovo value_type, ovviamente – MSalters

2

Utilizzando Boost Lambda, si potrebbe provare qualcosa di simile. La versione che ho di Boost Lambda, questo in realtà non funziona, proverò e correggerò più tardi.

#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

Solo di passaggio, ma questo ha fatto il lavoro per me, in modo che possa per qualcun altro (versione tagliata):

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

Caso d'uso dato:

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; 

}); 

- - Va bene con C++ 11

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

più semplice e universale!