2011-05-29

c++のコンテナをストリームに出力するコードスニペット

どういうことがしたいかというと、

for (vector<string>::iterator it = lis.begin(); it != lis.end(); ++it) {
  cout << *it << endl;
}

のようなループをかくのが面倒なので

cout << lis << endl;

のように書きたい。

これをやるにはtemplateパラメータにtemplateをつかってoperator<<をオーバーロードしてやればまあまあ簡単にかける。

#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <set>
using namespace std;
template <typename T, typename U>
ostream& operator<<(ostream& os, const pair<T, U> p) {
os << p.first << ':' << p.second;
return os;
}
template <typename T, typename Alloc,
template <typename _1, typename _2> class Container>
ostream& operator<<(ostream& os, const Container<T, Alloc >& p) {
typename Container<T, Alloc>::const_iterator e = p.end();
os << '[';
for (typename Container<T, Alloc>::const_iterator it = p.begin(); it != e;) {
os << *it;
os << (++it == e ? "" : ", ");
}
os << ']';
return os;
}
template <typename T, typename Alloc,
template <typename _1, typename _2, typename _3> class Container>
ostream& operator<<(ostream& os, const Container<T, less<T>, Alloc>& p) {
typename Container<T, less<T>, Alloc>::const_iterator e = p.end();
os << '[';
for (typename Container<T, less<T>, Alloc>::const_iterator it = p.begin(); it != e;) {
os << *it;
os << (++it == e ? "" : ", ");
}
os << ']';
return os;
}
template <typename T, typename Alloc,
template <typename _1, typename _2, typename _3> class Container>
ostream& operator<<(ostream& os, const Container<T, greater<T>, Alloc>& p) {
typename Container<T, greater<T>, Alloc>::const_iterator e = p.end();
os << '[';
for (typename Container<T, greater<T>, Alloc>::const_iterator it = p.begin(); it != e;) {
os << *it;
os << (++it == e ? "" : ", ");
}
os << ']';
return os;
}
template <typename T, typename U, typename Compare, typename Alloc,
template <typename _1, typename _2, typename _3, typename _4> class Container>
ostream& operator<<(ostream& os, const Container<T, U, Compare, Alloc>& p) {
typename Container<T, U, Compare, Alloc>::const_iterator e = p.end();
os << '{';
for (typename Container<T, U, Compare, Alloc>::const_iterator it = p.begin(); it != e; ) {
os << *it;
os << (++it == e ? "" : ", ");
}
os << '}';
return os;
}
int main() {
vector<int> ary;
ary.push_back(1);
ary.push_back(2);
ary.push_back(3);
cout << ary << endl; // [1, 2, 3]
set< string > lis1;
lis1.insert("foo");
lis1.insert("bar");
lis1.insert("baz");
cout << lis1 << endl; // [bar, baz, foo]
map<int, vector<int> > lis2;
lis2.insert(make_pair(1, ary));
ary.pop_back();
lis2.insert(make_pair(2, ary));
ary.clear();
lis2.insert(make_pair(3, ary));
cout << lis2 << endl; // {1:[1, 2, 3], 2:[1, 2], 3:[]}
}
view raw a.cc hosted with ❤ by GitHub

forward_iteratorを実装してるコンテナならなんでもいけるはず。ネストしててもOK

テンプレートパラメータが3つのコンテナ用の定義でlessとgreaterに分けてるのは、コンパイラに型推論をうまくやってもらうため。(テンプレートパラメータだけではstringの型と見分けがつかないので)


2011-06-04追記
古いコンパイラだとコンパイルできないかも

ZenBackWidget