set, multiset, priority_queue などでは,比較関数を指定することができる. また,vector の sort でも比較関数を指定することができる. この指定の仕方はなかなかに面倒であるように思う.
1. set, multiset, priority_queue などの比較関数
1.1. 降順
大きい順に要素を並べたい場合には,greater<T>
を指定すれば良い.
例:
set<int, greater<int>> xs;
注意:
コンストラクタにはパラメタを渡す必要は無いが, 渡すのなら丸括弧でなく波括弧で渡す必要がある.
set<int, greater<int>> xs{greater<int>()}; // OK
set<int, greater<int>> xs(greater<int>()); // NG
丸括弧を使うと,「greater
1.2. ラムダ式 lf を比較関数として使う
テンプレートパラメタとして decltype(lf)
を,
コンストラクタのパラメタとして lf
を用いる.
例:
auto lf = [](int a, int b) -> bool { return a > b; };
set<int, decltype(lf)> xs{lf};
注意: コンストラクタに f
を渡さないと,コンパイルエラーになる.
decltype(f)
(ラムダ式) のコンストラクタは削除されているということらしい.
1.3. 普通の関数 f を比較関数として使う
テンプレートパラメタとして decltype(&f)
を,
コンストラクタのパラメタとして f
を用いる.
例:
bool f(int a, int b) { return a > b; }
...
set<int, decltype(&f)> xs(f);
// set<int, bool (*)(int, int)> xs(f); としても同じ
注意: テンプレートパラメタの &
を忘れないこと.実際に渡される f は関数ポインタであるから,
関数のクラス delctype(f)
ではなく,
関数ポインタのクラス decltype(&f)
になる,ということらしい.
注意: コンストラクタに f を渡さないと,コンパイルは通るが,実行時エラー (SEGV) になる.
これは,decltype(f)
から関数ポインタを作成するが,それが f
で初期化されないから,ということらしい.
1.3.1 構造体で包む
何らかの事情で,引数のないコンストラクタを使いたい場合,構造体をつかう.
struct MyComp {
bool operator()(int a, int b) const { return a > b; }
};
...
set<int, MyComp> xs;
- const 指定子を忘れないこと
- ローカル変数の値に依存した比較はできないように思われる.
struct MyComp
をローカルにすることはできるが,operator()
の中でローカル変数にアクセスするとエラーに なるようだ. - 「何らかの事情」の例として,
vector<set<int, MyComp>> vsi(n);
のようにしたい場合があるかと思ったが,vector vsi(n, set<int, decltype(&f)>{f});
とすれば不要だった.そういう事情はないかもしれない.
1.4 テンプレートパラメタを指定しない (上記共通)
この節の内容については,tatyamさんに教えていただきました (これ と これ ). ありがとうございました.
set のコンストラクタには,第1引数に initializer_list を,第2引数に Compare オブジェクトを指定するものがあるので, それを用いれば,テンプレートパラメタを指定しないですむ.
set xs(initializer_list<int>{}, greater<>{});
set xs(initializer_list<int>{}, f);
set xs(initializer_list<int>{}, lf);
set xs(initializer_list<int>{}, MyComp{});
initializer_list が空でなければ,そこも推論してもらえることもある.
set xs({0, 1, 2}, greater<>{});
set xs({0, 1, 2}, f);
set xs({0, 1, 2}, lf);
set xs({0, 1, 2}, MyComp{});
2. sort() での比較関数と,{lower|upper}_bound()
関数 sort() は,第3引数に比較関数を渡すことができる.普通の関数でもラムダ式でも OK. lower_bound() や,upper_bound() の第4引数に,sort で使った比較関数を与えることで, その順序に関する二分探索を実行することができる.
bool f(int a, int b) { return a > b; }
...
vector<int> vec;
...
// 以下の3行はどれも同じ結果を与える:
auto mycomp0 = greater<int>();
auto mycomp0 = f;
auto mycomp0 = [](int a, int b) -> bool { return a > b; };
sort(vec.begin(), vec.end(), mycomp0);
auto it = lower_bound(vec.begin(), vec.end(), x, mycomp0);