はじめに
C++は可読性も作業効率も(C言語に比べ)高くかけます
が、C言語の記述方法も可能なため、なかなかC++っぽく書けない人が多いです
C++でちゃんと書けば速度はほとんど落とす事なく、安全なコードがかけます
今回は、C++11ではじめて作業をして、とても快適になった
std::algorithm のさわりを。
何が便利になったか
std::algorithmは、関数(オブジェクト)のコールバックをよく使いますが
C++11以前は、関数を作成し関数ポインタを渡すか、関数オブジェクトを作成する
必要があり、少し手間に感じていました
が、C++11では ラムダ関数が標準化され、関数オブジェクトが非常に簡単に作れます
ラムダ関数の登場により、std::algorithmは恩恵をかなり受け、使いやすくなりました
// 関数ポインタ例
bool func(int &n){
return(...);
}
auto fp = std::find( v.begin(), v.end(), (bool(*)(int))func );
...
// 関数オブジェクト例
struct OBJ{
bool operator()(int n) const{
return(...);
}
};
auto fp = std::find_if( v.begin(), v.end(), OBJ() );
...
// ラムダ
auto fp = std::find_if( v.begin(), v.end(), [](int &n){ return(...); } );
このように、ラムダだと非常にすっきりしますね!
さらに、他の変数の値を保持させる場合は
関数ポインタでは難しいです
関数オブジェクトでは、引数付きコンストラクタを作成し値を保持しますが
ラムダでは、[]内部に変数名を書くだけで、値の保持(コピー、参照も選べる)が出来
非常に便利です
find_if C言語で愚直に
find_ifは、検索の開始と終了を指定し
関数(オブジェクト)の条件がtrueだった最初のイテレータを返します
今回は HOGE構造体の flag==trueかつ、idの値が一致するものを取得します
まずは愚直に C言語での実装法
struct HOGE{
int id;
bool flag;
};
// C言語の書き方
HOGE h[]={ {1,true},{2,true},{3,false} };
for(int i=0; i<3; i++){
if( (h[i].id== 1) && (h[i].flag==true)){
std::cout << "for h" << std::endl;
}
}
愚直でCらしいですね!
std::arrayでC言語風に愚直で
C++11では配列は std::arrayを使います。
生配列とちがい、サイズが取得出来たり、STLコンテナとして使えたり
生配列よりも安全かつ、オーバーヘッド無しで配列と同じように使えるので
今まで生配列を使っていた人はこの機会に stl::arrayに移行しましょう
(現在は 初期化時に配列数を明示的に指定する必要があるなど 多少面倒ですが、std::make_arrayが次に導入されるので より便利になります)
struct HOGE{
int id;
bool flag;
};
// std::arrayを愚直にC言語で
std::array<HOGE,3> hh = {{ {1,true},{2,true},{3,false} }};
for(int i=0; i<hh.size(); i++){
if( (hh[i].id== 1) && (hh[i].flag==true)){
std::cout << "for hh" << std::endl;
}
}
array<>::size() で配列の個数を取得出来る以外は、同じですね。
実際の動作もオーバーヘッドなく生配列と同じなので、速度も問題なく使えます
一つ不思議なのは、初期化時に なぜか {} が一つ多い事。
理由はわかりませんが、arrayを使う場合は、一つ多く囲わなければダメのようです
for_each
C++で要素をなめる時は std::for_eachが使えます
struct HOGE{
int id;
bool flag;
};
std::array<HOGE,3> hh = {{ {1,true},{2,true},{3,false} }};
// std::for_each
std::for_each(hh.begin(), hh.end(),
[](HOGE &h){
if((h.id==1)&&(h.flag==true)){
std::cout << "std::for_each hh" << std::endl;
break;
}
} );
for_eachの第一引数は、開始イテレータ、第二引数は終了イテレータ
第三引数に、実行する関数(オブジェクト)を指定します
少し STLっぽくなってきましたね
range-based for-loop
std::for_each と似た動作をするものとして range-based for-loopがあります
日本語だと 範囲ベースforループ とか、よくわからない名前なので英語でいきます
std::for_eachに比べ、非常にすっきりした文法で全ての要素をなめます
std::for_eachだと 始めと終わりを指定出来るので、そういう用途の場合はそちらを使うべきですね
struct HOGE{
int id;
bool flag;
};
std::array<HOGE,3> hh = {{ {1,true},{2,true},{3,false} }};
// range-based for-loop
for( auto &a: hh){
if( (a.id== 1) && (a.flag==true)){
std::cout << "range-based for hh" << std::endl;
break;
}
}
関数オブジェクトもなく、簡潔にかけます。
非常に便利なので 使ってみて下さい!
std::find_if
やっと本命です
std::find_ifは、第一引数 開始イテレータ、第二引数 終了イテレータ
第三引数 条件 関数(オブジェクト)
となっております
struct HOGE{
int id;
bool flag;
};
std::array<HOGE,3> hh = {{ {1,true},{2,true},{3,false} }};
// std::find_if
auto fi = std::find_if(hh.begin(), hh.end(),
[](HOGE &h){
return( (h.id==1)&&(h.flag==true));
} );
if(fi != hh.end()){
std::cout << "std::find_if" << std::endl;
}
std::for_each に似た形ですが、第三引数には条件式を渡します
この条件式が trueの場合は find_ifをそこで停止し、trueになった時のイテレータが返却されます
どれにも一致しなかった場合は end() が返ります
おそらく、最もSTLの恩恵を授かったコードになっていると思います
番外編 std::find
std::findは std::find_ifと違い、第三引数は条件関数オブジェクトではなく、値を指定します
例えば コンテナの中身が int等の場合は、数字を指定し一致したイテレータが取得できます
今回は 構造体なので、そのまま値を比較できないので
operator int() を実装し値の比較をします
(内部は-1というダミー値を使い ダサいコードですが・・)
struct HOGE{
int id;
bool flag;
operator int() {
if(flag) {
return id;
}else{
return -1;
}
};
};
std::array<HOGE,3> hh = {{ {1,true},{2,true},{3,false} }};
// std::find
auto f = std::find(hh.begin(), hh.end(), 1);
if(fi != hh.end()){
std::cout << "std::find" << std::endl;
}
あるいは、intにせず構造体で比較するには、operator==をオーバーライドし
struct HOGE{
int id;
bool flag;
bool operator ==(const HOGE &h) {
return( (h.flag==true)&&(this->id==h.id) );
}
};
std::array<HOGE,3> hh = {{ {1,true},{2,true},{3,false} }};
// std::find
auto f = std::find(hh.begin(), hh.end(), HOGE{1,true});
if(f != hh.end()){
std::cout << "std::find" << std::endl;
}
実行速度とか多少オーバーヘッドありそうだけど、operator==をオーバーロードすると
すんなり書く事も出来ます
が
と複数条件のfindは find_ifを使うのが良いと思います
ってことで、 std::find、std::find_if
今回はじめてSTLをマトモに仕事に使って 非常に便利だったので
オススメしておきます
また findは、setやmap、unordered_map等の場合はメンバ関数のものを使うと
二分木やハッシュを使い検索してくれるので
そちらを使って下さい!