とくにあぶなくないRiSKのブログ

危ないRiSKのブログだったかもしれない。本当はRiSKだけどググラビリティとか取得できるIDの都合でsscriskも使ったり。

on を使って 汎用 LessBy をさらに汎用にする

元ネタ

汎用LessBy - p_tanのお勉強日記
やっていることは on だと思いました。
Data.Function

on :: (b -> b -> c) -> (a -> b) -> a -> a -> c

なので C++ っぽく翻訳すると

template<class A, class B, class C>
std::function<C (A, A)> on(std::function<C (B, B)> f, std::function<B (A)> g);

GetA を使うことを考えると
f は [](int a, int b){ return a < b; } にあたります。
g は &X::GetA にあたります。(&X::GetA の型は int (X::*)() ですが,変形すると int (X) という関数型にできます。X があれば int を得られるという点で同じですね。)
で,返却値が LessBy です。

C++ で on と言えば

C++ で一般化された on を書く - iorateの日記
これですね。
しかし,問題があります。この on は INVOKE に対応していないので,メンバ関数へのポインタを渡すとこけます。INVOKE 対応にしましょう。std::bind は INVOKE に対応しているので std::bind に丸投げすれば OK。

こうなった

コード

#include<functional>
#include<utility>
#include<type_traits>

namespace detail{

template<class F, class G>
struct on_result
{
 F m_f;
 G m_g;

 template<class... Args>
 auto operator()(Args&&... args) const
  -> decltype(std::bind(m_f, std::bind(m_g, std::forward<Args>(args))()...)())
 {
  return std::bind(m_f, std::bind(m_g, std::forward<Args>(args))()...)();
 }
};

}

template<class F, class G>
inline auto on(F &&f, G &&g)
 -> detail::on_result<typename std::decay<F>::type, typename std::decay<G>::type>
{
 return {std::forward<F>(f), std::forward<G>(g)};
}

#include<iostream>
#include<vector>
#include<algorithm>
#include<boost/lambda/lambda.hpp>

class X
{
 int a;
 double b;
public:
 X(int a, double b)
  : a(a), b(b)
 {}
 int GetA() const { return a; }
 double GetB() const { return b; }
 friend std::ostream& operator<<(std::ostream& os, X const & x);
};

std::ostream& operator<<(std::ostream& os, X const & x)
{
 return os << '{' << x.a << ", " << x.b << '}';
}

int main()
{
 using boost::lambda::_1;
 using boost::lambda::_2;

 std::vector<X> v{
  {3, 1.7},
  {1, 3.2},
  {4, 0.5},
  {1, 0.8}
 };

 std::sort(v.begin(), v.end(), on(_1 < _2, &X::GetA));
 // ↑は↓とほぼ同じ
 // std::sort(v.begin(), v.end(), on([](int a, int b){return a < b;}, &X::GetA));
 std::for_each(v.begin(), v.end(), [](X const & x){std::cout << x << '\n';});

 std::cout << "**********\n";

 std::sort(v.begin(), v.end(), on(_1 < _2, &X::GetB));
 // ↑は↓とほぼ同じ
 // std::sort(v.begin(), v.end(), on([](double a, double b){return a < b;}, &X::GetB));
 std::for_each(v.begin(), v.end(), [](X const & x){std::cout << x << '\n';});
}

実行結果:

{1, 3.2}
{1, 0.8}
{3, 1.7}
{4, 0.5}
**********
{4, 0.5}
{1, 0.8}
{3, 1.7}
{1, 3.2}

いい感じにできましたね。