2009-03-11 16 views
16

を私は次のコードを持っている:私はちょうど出力ストリームにマップをコピーする場合は、このコードではのstd ::コピー先はstd :: coutでのstd ::ペア

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

を。このために私は演算子を定義する必要があります< <(..) - OK。 しかし、コンパイラが私のオペレータ< <()を見つけることができません。
ためのstd :: coutを、STD ::ペアと私のオペレータ< <と呼ばれるのstd ::コピー - すべての名前空間stdから。

クイックソリューション - 私のエイラーを< <に追加してください。しかし、それは醜いです。

この問題の解決方法または回避策はありますか。

+0

ところで、最初のfor-loopを実行するSTLアルゴリズムがあります。http://www.sgi.com/tech/stl/generate.htmlを参照してください。ランダムな値にランダムな位置、http://www.sgi.com/tech/stl/random_shuffle.html。 – paxos1977

答えて

13

私は、この問題を解決するための新しいエレガントな方法を確立しました。
私は答えを読んだとき、多くの関心のアイデアを持っている:

  • ラップイテレータを、ためのstd ::を変換はstd ::ペアにする文字列を、
  • wrap std :: pair、演算子をオーバーロードするチャンスがあるため< <);
  • 印刷ファンクタで通常のstd :: for_eachを使用します。
  • boost :: labdaでstd :: for_eachを使用すると、std :: pairへのアクセスを除いてniceと見えます。< :: firstとstd :: pair < :: :: second members;

私は将来この他の問題を解決するためにこのアイデアをすべて使用すると思います。
この場合、私は "地図のデータを文字列に変換して出力ストリームに書き込む"という代わりに "マップのデータを出力ストリームにコピーする"という私の問題を定式化することができます。私のソリューションは次のようになります:

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

Michaelのソリューションは意図的にtoStringを避けるようにして、他のtoString定義と衝突しないようにしています。 )ここで最善の答えより短くて表現力豊かな主張は間違っています。 – codetaku

16

はよく、あなたはそれが印刷する方法を次の男はそれを望んでいる途中から、おそらく異なっている、のでstd::pairをCOUTする標準的な方法はありません。これは、カスタムファンクタまたはラムダ関数の良いユースケースです。その後、それを引数として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ライブラリへの合理的な拡張機能ですので、私は、これは1時間の事である場合は特に、std名前空間にそれを置くと思います。他の誰かが別の場所で同じことをすると、リンカーエラーを引き起こさないように静的に宣言することができます。

頭に浮かぶもう一つの解決策は、の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、非常にいい - 変換コンストラクタは、必要に応じてペアを自動的にpairWrapperに変換します。しかし、テンプレート演算子<<()にostream&パラメータを追加してください。 –

+0

ニース、ありがとう。 – bayda

+0

間違っている、許可されていない。名前空間stdは、コンパイラ提供のクラス、テンプレート、および関数用です。過負荷を追加することはできません。 – MSalters

10

をC++標準(セクション17.4.3.1を参照)。

+0

重要なお知らせ、ありがとうございます。 – bayda

+0

例外: 'std :: swap'のオーバーロードを追加することができます。 –

+0

@konradあなたはそれについての参考文献を持っていますか? –

5

あなたが欲しいのは、変形イテレータです。このようなイテレータは、別のイテレータをラップし、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 + "]"; 
    } 
... 
+0

ありがとうiteratorラッパーを作成する良いアイデアは、このアイデアは一般化され、他の問題を解決するために使用される可能性があります – bayda

+0

一般的な何かをしたい場合、1つの明白なステップは、適切なboost :: functionに変換を格納することです。もちろん、新しいvalue_typeのテンプレートパラメータを追加する必要があります – MSalters

2

、あなたはこのような何かを試みることができます。 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; 

もっとシンプルでユニバーサル!

関連する問題