multi_indexが便利な件について その2
前回のmulti_indexの話の続きです。
multi_indexを作ってみる!
ここでは3つのインデックスをもつmulti_indexを作ってみます。
ちょっと複雑というか、長いコードなのでびっくりするかもしれませんが
落ち着いて読めば大丈夫です。
typedef multi_index_container< Charactor, indexed_by< // 挿入順アクセス sequenced<>, // Charactor::operatro<順でアクセス ordered_unique<identity<Charactor> >, // Charactor::nameの順番 ordered_non_unique<member<Charactor,std::string,&Charactor::name> > > > charactor_set;
単なるtypedef文です。
typedef multi_index_container<テンプレートパラメータ> charactor_set;
という文なわけですね。
じゃあ、multi_index_containerのテンプレートパラメータはそれぞれどういう意味なの?ということですが
本来は3つテンプレートパラメータがあって
multi_index_container< 挿入したいオブジェクトの型 , ソートの方法 , アロケータ >
となっています。
3番目のアロケータは省略が可能ですのでここでは説明を省略します。
気になる方は公式のドキュメントを参照してください。
ここで大事なのは2番目のパラメータである
ソートの方法です。
これにはindex_by<>で括った複数のソート方法を指定することができます。
上のtypedef文の場合ですと
indexed_by< // 挿入順アクセス sequenced<>, // Charactor::operatro<順でアクセス ordered_unique<identity<Charactor> >, // Charactor::nameの順番 ordered_non_unique<member<Charactor,std::string,&Charactor::name> > >
と3種類指定しています。
アクセスの方法!
宣言は先ほどの例のとおりです。
では、どうやってアクセスすることが出来るのかを示します。
charactor_set charactors; // キャラクタを順番に追加 charactors.push_back( Charactor(0,0,"d")); charactors.push_back( Charactor(1,0,"c")); charactors.push_back( Charactor(3,0,"a")); charactors.push_back( Charactor(2,0,"b")); // まずは、挿入順でアクセス std::cout << "orderd by inserted\n"; charactor_set::nth_index<0>::type& c0 = charactors.get<0>(); BOOST_FOREACH( const Charactor& s, c0 ) { s.print(); } std::cout<<std::endl; // operator<の順番でアクセス std::cout << "orderd by operator<\n"; charactor_set::nth_index<1>::type& c1 = charactors.get<1>(); BOOST_FOREACH( const Charactor& s, c1 ) { s.print(); } std::cout<<std::endl;
上記のコードを実行した結果としては
orderd by inserted [name:d , x:0 , y:0] [name:c , x:1 , y:0] [name:a , x:3 , y:0] [name:b , x:2 , y:0] orderd by operator< [name:d , x:0 , y:0] [name:c , x:1 , y:0] [name:b , x:2 , y:0] [name:a , x:3 , y:0]
のような出力が得られます。
確かに、最初の出力では挿入された順番、2番目の出力ではx順番と複数のソート順で出力されていますね!
コードの中で注目するべきところは
charactor_set::nth_index<1>::type& c1 = charactors.get<1>();
この部分ですね。
charactor.get<>でindex_by<>で指定したソート順になっているコンテナを取得できます。
テンプレート引数には数字を与えることができ、その数字はindex_byで指定したソート順の
番号です(0から始まります)。
コンテナに収められている型情報はcharactor_set::nth_index<>::typeで取得できます。
ここでの注意点はgetは参照を返すということですね。
まとめ
駆け足ですがmulti_index_container<>の紹介をしてみました!
足りない情報満載ですが、そこは各自調べてください。
簡単ですがまとめておきます。
multi_index_container<>を利用することで
複数のインデックスをもったコンテナを宣言することができます。
multi_indexcontainer<>のテンプレート引数は
- 格納したい型
- index_by<>で囲まれた、ソートしたい順番のリスト
を指定する。
get<数字>を呼ぶことで、index_byの中の指定したソート順になっているコンテナを取得できる。
数字は0から始まる。
まとめると簡単ですね!
これで快適な生活をぜひ!
おまけ
実行できる(と思う)サンプルコードを載せておきます。
#include <algorithm> #include <iterator> #include <ostream> #include <iostream> #include <string> #include <boost/multi_index_container.hpp> #include <boost/multi_index/sequenced_index.hpp> #include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/key_extractors.hpp> #include <boost/foreach.hpp> using namespace std; using namespace boost::multi_index; struct Charactor { int x; int y; std::string name; Charactor(int x,int y,const std::string& name):x(x),y(y),name(name){} // 標準の順序は、xの大きさで決定する bool operator<(const Charactor& e) const { return x < e.x; } // デバッグ用の出力関数 void print() const { std::cout << "[name:" << this->name << " , x:" << this->x << " , y:" << this->y << "]" << std::endl; } }; typedef multi_index_container< Charactor, indexed_by< // 挿入順アクセス sequenced<>, // Charactor::operatro<順でアクセス ordered_unique<identity<Charactor> >, // Charactor::nameの順番 ordered_non_unique<member<Charactor,std::string,&Charactor::name> > > > charactor_set; int main(int argc,char** argv) { // multi_indexを作成 charactor_set charactors; // キャラクタを順番に追加 charactors.push_back( Charactor(0,0,"d")); charactors.push_back( Charactor(1,0,"c")); charactors.push_back( Charactor(3,0,"a")); charactors.push_back( Charactor(2,0,"b")); // まずは、挿入順でアクセス std::cout << "orderd by inserted\n"; charactor_set::nth_index<0>::type& c0 = charactors.get<0>(); BOOST_FOREACH( const Charactor& s, c0 ) { s.print(); } std::cout<<std::endl; // operator<の順番でアクセス std::cout << "orderd by operator<\n"; charactor_set::nth_index<1>::type& c1 = charactors.get<1>(); BOOST_FOREACH( const Charactor& s, c1 ) { s.print(); } std::cout<<std::endl; // Charactor::name順でのアクセス std::cout << "orderd by name\n"; charactor_set::nth_index<2>::type& c2 = charactors.get<2>(); BOOST_FOREACH( const Charactor& s, c2 ) { s.print(); } std::cout<<std::endl; // 削除してみる c2.erase("a"); // c2の変更がc1にも波及していか確認 std::cout << "after deleted" << std::endl; BOOST_FOREACH( const Charactor& s, c1 ) { s.print(); } std::cout<<std::endl; BOOST_FOREACH( const Charactor& s, c2 ) { s.print(); } std::cout<<std::endl; }
簡単なデモなのですが長いコードになってしまいましたね…。
このプログラムを実行した時に実行結果は
orderd by inserted [name:d , x:0 , y:0] [name:c , x:1 , y:0] [name:a , x:3 , y:0] [name:b , x:2 , y:0] orderd by operator< [name:d , x:0 , y:0] [name:c , x:1 , y:0] [name:b , x:2 , y:0] [name:a , x:3 , y:0] orderd by name [name:a , x:3 , y:0] [name:b , x:2 , y:0] [name:c , x:1 , y:0] [name:d , x:0 , y:0] after deleted [name:d , x:0 , y:0] [name:c , x:1 , y:0] [name:b , x:2 , y:0] [name:b , x:2 , y:0] [name:c , x:1 , y:0] [name:d , x:0 , y:0]