注意: 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