C++ の std::vector に v += 3, 1, 4 で push_back
boost::assign使ってみた - Faith and Brave - C++で遊ぼう にて boost::assign の使い方が紹介されていた。
#include <iostream>
#include <vector>
#include <shand/foreach.hpp>
#include <boost/assign/std/vector.hpp>
using namespace std;
using namespace boost::assign;
int main()
{
vector<int> v;
v += 3, 1, 4; // !!!
for(unsigned int i = 0; i < v.size(); i++)
cout << v[i] << endl;
return 0;
}
おおおおお、なんかすごい…!
普通なら
v.push_back(3);
v.push_back(1);
v.push_back(4);
と書かなきゃならないところを
v += 3, 1, 4;
と書ける。
自分の C++ の常識からするとびっくりするような書き方なのだけど、C++ ではカンマ演算子は一番優先度が低く設定されていて、
((v += 3), 1), 4;
という順番で実行されるわけだ。+= 演算子では、戻り値として list_inserter クラスが返されていて、これのカンマ演算子がオーバーロードされているらしい。
boostのソースを再現
試しに、boost のソースに当たってみたが、テンプレート使いまくりで理解が困難だったので、このサンプルを動かすのに必要な機能だけを抽出してみた。
#include <iostream>
#include <vector>
using namespace std;
// list_inserter クラスの定義
template<class T>
class list_inserter
{
private:
std::vector<T>& c;
public:
list_inserter( vector<T>& c ) : c( c)
{}
list_inserter& operator,( const T& r )
{
c.push_back( r );
return *this;
}
list_inserter& operator()( const T& t )
{
c.push_back( t );
return *this;
}
};
// += 演算子の定義
template< class T >
inline list_inserter<T>
operator+=( std::vector<T>& c, T v )
{
return list_inserter<T>( c )( v );
}
operator+=( std::vector
結果として、次のカンマで list_inserter のカンマ演算子オーバーロードが呼ばれて、vector に push_back されるというわけだ。
list_inserter を汎用的に
上の list_inserter は型を vector
どうやってるかというと、コンテナに要素を追加するためのクラスが用意されている。
- vector の場合は boost::assign_detail::call_push_back
- map の場合は boost::assign_detail::call_insert
といった具合。
list_inserter では、このクラス経由でコンテナに追加している。上の list_inserter で c.push_back( t ) と決めうちしてる部分が、追加するためのクラスのメソッド呼び出しに変わるわけだ。
この仕組みのおかげで、コンテナの実体が vector なのか map なのかによらず、同じコードで実装できるようになる。デザパタでいうところの Strategy パターンってとこだろうか。