2009-03-11 6 views
16

ich nächste Code haben:std :: copy std :: cout für 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; 
} 

In diesem Code I Karte Ausgabestrom nur kopieren möchten. Dazu brauche ich definieren Operator < < (..) - OK. Aber nach Namen finden Regeln Compiler kann nicht finden, mein Operator < <().
Weil std :: cout, std :: pair und std :: Kopie, die mein Operator < < genannt - alles aus Namensraum std.

Schnelle Lösung - fügen Sie meinen oerator < < zu Std Namespace - aber es ist hässlich, imho.

Welche Lösungen oder Workarounds für dieses Problem kennen Sie?

+0

BTW, gibt es einen STL-Algorithmus, der die erste for-Schleife, http://www.sgi.com/tech/stl/generate.html erreichen wird, dann, wenn Sie wirklich das Gefühl haben, dass die zufälligen Werte zugewiesene zufällige Positionen benötigen , http://www.sgi.com/tech/stl/random_shuffle.html. – paxos1977

Antwort

13

Ich habe eine neue elegante Möglichkeit zur Lösung dieses Problems gegründet.
Ich habe viele Ideen Interesse bekam, wenn Antworten lesen:

  • Wrap Iterator für Transformation std :: pair std :: string;
  • Wrap std :: pair, haben für eine Chance Betreiber zu überlasten < < (...);
  • verwenden Sie die üblichen std :: for_each mit Druck Funktor;
  • verwenden Sie std :: for_each mit boost :: labda - sieht nett aus, außer Zugriff auf std :: pair <> :: erste und std :: paar <> :: zweite Mitglieder;

Ich denke, ich werde all diese Ideen in Zukunft für die Lösung verschiedener anderer Probleme verwenden.
Aber für diesen Fall habe ich untertrieben, dass ich mein Problem als "Umwandlung von Daten in Zeichenfolgen und Schreiben in den Ausgabestream" anstatt "Kopieren von Daten der Karte in den Ausgabestream" formulieren kann. Meine Lösung sieht so aus:

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

Ich denke, diese Methode ist am kurzesten und expressiver als andere.

+0

Michaels Lösung vermeidet absichtlich toString, so dass es keinen Konflikt mit anderen toString-Definitionen gibt.Auch ist er bei mehreren Verwendungen kürzer als deins (die Transformation benötigt einen zusätzlichen Parameter) Also ist Ihr Anspruch, hier kürzer und expressiver zu sein als die beste Antwort hier, fehlgeleitet. – codetaku

16

Es gibt keinen Standard Weg, um eine std::pair zu cout, weil, na ja, wie Sie es gedruckt wollen, ist wahrscheinlich anders als die Art, wie der nächste Kerl es will. Dies ist ein guter Anwendungsfall für eine benutzerdefinierte Funktor- oder Lambda-Funktion. Sie können das als Argument an std::for_each übergeben, um die Arbeit zu erledigen.

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

Um diesen Funktor aus dem Code aufrufen:

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

[ich eher diese Antwort löschen würde, aber ich werde es jetzt verlassen, falls jemand die Diskussion interessant findet.]

Da es eine angemessene Verlängerung der std Bibliothek, würde ich es nur in std-Namespace setzen, vor allem, wenn diese eine Sache, eine Zeit ist. Sie können es einfach als statisch deklarieren, um zu verhindern, dass es Linker-Fehler verursacht, sollte jemand anders dasselbe an einem anderen Ort tun.

Eine andere Lösung, die den Sinn kommt, ist ein Wrapper für std :: pair zu erstellen:

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, sehr schön - der Converting-Konstruktor wird automatisch ein Paar in einen PairWrapper konvertieren, wenn nötig. Aber bitte fügen Sie den Ostream & Parameter zu Ihrem Vorlagenoperator <<() hinzu. –

+0

Schön, danke. – bayda

+0

Falsch, nicht erlaubt. Namespace std steht für vom Compiler bereitgestellte Klassen, Vorlagen und Funktionen. Sie dürfen keine Überladungen hinzufügen. – MSalters

10

Ich mag möchte nur, dass nach illegale Dinge weisen darauf hin, std :: Namespace ist das Hinzufügen zu der C++ Standard (siehe Abschnitt 17.4.3.1).

+0

Wichtiger Hinweis, danke. – bayda

+0

Eine Ausnahme: Sie können Überladungen für 'std :: swap' hinzufügen. –

+0

@konrad Sie haben eine Referenz dafür? –

5

Was Sie wollen, ist ein transformierender Iterator. Diese Art von Iterator umschließt einen weiteren Iterator, leitet alle Positionierungsmethoden wie operator ++ und operator == weiter, definiert aber operator * und operator-> neu.

Schnellskizze:

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

Danke. Schöne Idee, Iterator Wrapper zu erstellen, könnte diese Idee verallgemeinert und für die Lösung anderer Probleme verwendet werden. – bayda

+0

Wenn Sie etwas generisches wollen, ist ein naheliegender Schritt, die Transformation in einer geeigneten boost :: -Funktion zu speichern. Ich benötige natürlich einen zusätzlichen Template-Parameter für den neuen value_type – MSalters

2

Lambda Boost, könnten Sie so etwas wie dies versuchen. Die Version, die ich von Boost Lambda habe, funktioniert nicht, ich werde später testen und reparieren.

#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

gerade vorbei, aber dies hat den Job für mich, so kann es für jemand anderen (gekürzte Fassung):

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

Anwendungsfall gegeben:

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; 

}); 

- - Es ist gut, mit C++ 11

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

einfacher und universell!