std :: pairのstd :: copyをstd :: coutにコピー
-
10-07-2019 - |
質問
次のコードがあります:
#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;
}
このコードでは、マップを出力ストリームにコピーするだけです。これを行うには、定義演算子<!> lt; <!> lt;(..)-OKが必要です。
しかし、ルールを見つける名前コンパイラは、私の演算子を見つけることができません<!> lt; <!> lt;()。
std :: cout、std :: pair、std :: copyが私のoperator <!> lt; <!> lt;を呼び出したためです。 -すべて名前空間stdから。
クイックソリューション-私のoerator <!> lt; <!> lt;を追加std名前空間へ-しかしそれはいです、私見。
この問題の解決策または回避策は何ですか?
解決 2
この問題を解決する1つの新しいエレガントな方法を確立しました。
回答を読むと、多くの興味深いアイデアがあります:
- イテレータをラップします。std:: pairをstd :: stringに変換します。
- std :: pairをラップして、演算子をオーバーロードする機会がある<!> lt; <!> lt;(...);
- 通常のstd :: for_eachと印刷ファンクターを使用します;
- boost :: labdaでstd :: for_eachを使用-std :: pair <!> lt;にアクセスする以外は見た目が良い<!> gt; :: firstおよびstd :: pair <!> lt; <!> gt; :: second members;
他のさまざまな問題を解決するために、今後これらすべてのアイデアを使用すると思います。
ただし、この場合、<!> quot;マップのデータを文字列に変換し、出力ストリーム<!> quotに書き込むことで問題を定式化できることを理解しました。代わりに、<!> quot;マップのデータを出力ストリーム<!> quot;にコピーします。私の解決策は次のようになります:
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 );
この方法は、他の方法よりも最も短く、表現力が高いと思います。
他のヒント
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));
C ++標準に従って、std ::名前空間に物事を追加することは違法であることを指摘したいだけです(セクション17.4.3.1を参照)。
必要なのは、変換イテレータです。この種のイテレーターは別のイテレーターをラップし、operator ++やoperator ==などのすべての位置決めメソッドを転送しますが、operator *およびoperator-<!> gt;を再定義します。
クイックスケッチ:
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 + "]";
}
...
ただ通り過ぎますが、これは私のために仕事をしたので、他の人(カット版)のためにできます:
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> ());
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 ) );
[この回答を削除したいが、誰かがディスカッションを面白く感じた場合に備えて、今のところは残しておきます。]
これはstdライブラリの合理的な拡張なので、特にこれが1回限りの場合は、std名前空間に配置するだけです。他の誰かが同じことを別の場所で行った場合に、それを静的エラーとして宣言してリンカーエラーを引き起こさないようにすることができます。
頭に浮かぶもう1つの解決策は、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) { ... }
for (const auto& your_pair : your_container)
your_stream << "[" << your_pair.first << "," << your_pair.second << "]" << endl;
よりシンプルで普遍的な!
for_each(some_map.begin(), some_map.end(), [](const std::map < size_t, size_t >::value_type &ite){
cout<<ite.first<<" "<<ite.second<<endl;
});
--- C ++ 11では問題ありません