コンテナの入れ替え

STLアルゴリズムにswapという関数がある。実装は簡単に書くと以下の通り。

template< class A >
inline void swap( const A& a1, const A& a2 ){
	const A temp( a1 );
	a1 = a2;
	a2 = temp;
}

自作しても簡単にすぐ作れるのだがこういった使用頻度が高い関数は例え短くても標準化されているほうがよいのは言うまでもない。さて、この関数はイコール演算子が定義されている型なら何でもよいのだが、一時オブジェクトを生成・コピーするので物によってはかなり重い場合がある。その典型的なのがコンテナ。空っぽならなんともないが、サイズがでかいとかなりパフォーマンスが悪い。

std::vector< int > a( 10000 );    //10000個のデータ
std::vector< int > b( 100000 );   //100000個のデータ
std::swap( a, b );                //中身を交換

//↓のように展開される
const std::vector< int > temp( a );  //←10000個分のメモリ確保・コピー
a = b;                               //←100000個分までメモリ拡張・コピー
b = a;                               //←10000個分のコピー

たかだか交換するだけでこんなにパフォーマンスを食うのは馬鹿らしい。ので私はコンテナの交換が発生するような場所ではコンテナを直接使わずに一旦ポインタにおいて間接的に使うようにしていた。

std::vector< int > a( 10000 );
std::vector< int > b( 100000 );
//↑こいつらは直接使わずに・・・
std::vector< int >* pa = &a;
std::vector< int >* pb = &b;
//↑一旦ポインタに置き換え、普段はこちらを使う。

(*pa)[3] = ・・・

std::swap( pa, pb );
//↑交換するのはポインタだけ。

とまあ、まどろっこしいマネをしていたのだが実はこんなことは不要だということに最近気がついた。
ひょっとしたらVSの方言かもしれないが、swapは以下のようなオーバーロードがされていた。

template< class A >
inline void swap( const std::vector< A >& a1, const std::vector< A >& a2 ){
	a1.swap( a2 );
}

初めて知ったのだが、vectorにはメンバとしてswapがあったらしい。他にも大抵のコンテナにはswapがあった。そして中身をたどってみたら、中のデータの先頭ポインタおよび必要なメンバだけを交換しており、一時オブジェクトの生成やコピーは発生しないように出来ていた。

わざわざ頑張らなくてもちゃんと用意がありました。もっと勉強しますorz