注意: std::bind1st, std::bind2nd, std::unary_function, std::binary_functionはC++11で非推奨機能となり、将来的には廃止される。
C++14 の Generic lambda で bind1st 書いてみた - C++でゲームプログラミング
実に面白い。ジェネリックラムダは、もちろんパラメーターパックが使えるので、大昔のbind1stは、以下のように実装できる。
#include <iostream> template<typename F, typename T> auto bind1st(F func, T t){ return [=](auto... args){ return func(t, args...); }; } int plus(int a, int b){ return a + b; } int main(){ auto plus3 = bind1st(&plus, 3); std::cout << plus3(5) << std::endl; std::cout << plus3(1) << std::endl; return 0; }
読者の大半は、すでにC++14ユーザーであろうから、このようなコードは当たり前であり、古き時代の苦しさは分からないだろう。そのため、C++03でbind1stを実装してみよう。
// C++03当時のコード #include <iostream> #include <functional> namespace ezoe { template <class Fn> class binder1st : public std::unary_function< typename Fn::second_argument_type, typename Fn::result_type> { protected: Fn op; typename Fn::first_argument_type value; public: binder1st( const Fn& x, const typename Fn::first_argument_type& y) : op(x), value(y) { } typename Fn::result_type operator()(const typename Fn::second_argument_type& x) const { return op( value, x ) ; } } ; template < class Operation, class T > binder1st<Operation> bind1st(const Operation& op, const T& x) { return binder1st<Operation>(op, typename Operation::first_argument_type(x)) ; } } // end namespace ezoe // 関数ポインターでは使えない struct plus : std::binary_function< int, int, int> { int operator ()( int a, int b ) const { return a + b ; } } ; int main() { auto plus3 = ezoe::bind1st(plus(), 3); std::cout << plus3(5) << std::endl; std::cout << plus3(1) << std::endl; return 0; }
まず、binder1stというクラスを書かなければならない。これはめんどくさい。しかも、このbinder1stは、規格の規定する関数オブジェクトしか使えないのだ。関数オブジェクトとは、std::binary_funcitonから派生するか、あるいは第一実引数、第二実引数、戻り値の型をそれぞれ表す、first_argument_type, second_argument_type, result_typeというネストされた型名を持たなければならない。そのため、C++03版のplusは、クラスで実装しなければならない。関数ポインターは渡せないのだ。
一応、仮引数の型を得る方法はあるが、それには複雑怪奇なテンプレートメタプログラミングを使わなければならない。
C++14版は、関数ポインターでも問題なく渡せる。
C++14、いい時代になったものだ。
No comments:
Post a Comment