p_tan's blog

勉強日記です。ツッコミ大歓迎

汎用LessBy

小さなクラスを特定のメンバの値を基準にしてソートしたい場合。
しかも、Boost.MultiIndexを使うのは大げさだなーという時。
C++11ならラムダ式で書けるが、それでもめんどくさい。
例:

class X{
    int a;
    double b;
public:
    // ...コンストラクタとか...

    int GetA() const { return a; }
    double GetB() const { return b; }
};

std::vector<X> v;
// X::aでソート
std::sort(v.begin(), v.end(), [](const X& lhs, const X& rhs){ lhs.GetA() < rhs.GetA(); }) 
// X::bでソート
std::sort(v.begin(), v.end(), [](const X& lhs, const X& rhs){ lhs.GetB() < rhs.GetB(); })

なので、関数で変換後の値を比較する汎用的なLessByがあると便利。

#include <functional>
template<class T, class F>
std::function<bool(const T&, const T&)> LessBy(F f)
{
    return [f](const T& lhs, const T& rhs)->bool{
        auto f_ = std::bind(f, std::placeholders::_1);
        return f_(lhs) < f_(rhs);
    };
}

使い方

///////////////////////////
// メンバ関数を使った変換
///////////////////////////
// X::GetAでソート
sort(v.begin(), v.end(), LessBy<X>(&X::GetA));
// X::GetBでソート
sort(v.begin(), v.end(), LessBy<X>(&X::GetA));

///////////////////////////////
// unary関数を使った変換
//////////////////////////////
int Transform(const &X);
// Transformで変換した値で元のオブジェクトをソート
sort(v.begin(), v.end(), LessBy<X>(&Transform));

GreaterByとかも作ろうと思ったら、比較の所を差し替えられるような高階関数を作ってやった方が良いんだろうなぁ。
関数の引数の型も推論させられればとかも書かなくて済むんだけど、メタプログラミング力が足りないのでやり方が分からない。

さらに言うと、このLessBy使ってsortすると関数が何度も評価されちゃうので、非常に効率悪そう。sortByは別にちゃんと最適化したものを作るべきかも。