アットウィキロゴ

単項関数オブジェクトを使ってみる


STLには関数オブジェクト(もしくはファンクションオブジェクトまたはファンクタ)と呼ばれるコンポーネントが存在します。
STLでは関数オブジェクトはアルゴリズムと共にもっとも作成する機会が多いコンポーネントです。
STLのアルゴリズムには引数に関数オブジェクトを取るものがあり、アルゴリズムの引数に渡す関数オブジェクトによりアルゴリズムを動作を変えることができるのです。
したがって、関数オブジェクトの種類によりアルゴリズムのバリエーションが増えることになります。
関数オブジェクトは基本クラスunary_functionまたは基本クラスbinary_function から継承しoperator()を実装しなければなりません。
パラメータが1つ(単項関数)の場合はunary_functionからパラメータが2つ(二項関数)の場合はbinary_functionからそれぞれ継承します。



unary_function基本クラスの定義

template<class _Arg, class _Result>
struct unary_function
{
   typedef _Arg argument_type;
   typedef _Result result_type;
};



unary_function・binary_functionはヘッダファイル<functional>に定義されているのでincludeを忘れないでください。
以下に関数オブジェクトのサンプルコードを示します。


例1

#include <functional>

template <class T>
struct OutputData : public unary_function<T, void>
{
   void operator()(argument_type _t) {
       cout << _t << ' ' ;
   }
};




関数オブジェクトOutputDataは基本クラスunary_functionから継承し、パラメータはclass Tになります。
基本クラスunary_functionにvoidを渡していますがこれは戻り値が存在しないことを表しています。
ここでは関数オブジェクトOutputDataに戻り値を使用していません。
例1は受け取ったパラメータ_tを標準出力に出力する関数オブジェクトです。

以下に関数オブジェクトOutputDataをアルゴリズムfor_eachに使用したサンプルコードを示します。



例2

#include <functional>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

template <class T>
struct OutputData : public unary_function<T, void>
{
   void operator()(argument_type  _t) {
       cout << _t << ' ' ;
   }
};

int main(int argc, char* argv[])
{
   const int N = 10;
   vector<int> v;

   for (int i = 0; i < N; i++)
       v.push_back(i);

   for_each(v.begin(), v.end(), OutputData<int>() );

   return 0;
}



実行結果
0 1 2 3 4 5 6 7 8 9



for_eachは第一引数から第二引数までの要素に第三引数である関数オブジェクトを適用するアルゴリズムです。
ここでは関数オブジェクトOutputDataを適用しているため全要素が標準出力に出力されます。
例えばアルゴリズムに渡す関数オブジェクトを「受け取った要素」をファイルに保存するような関数オブジェクトを渡すと全要素をファイルに保存してくれることになります。
このように適用する関数オブジェクトによりアルゴリズムのバリエーションが増えることになります。



二項関数オブジェクトを使ってみる


それでは次にパラメータを2つ取る関数オブジェクト(二項関数オブジェクト)を使ってみましょう。 
二項関数オブジェクトは基本クラスbinary_functionから継承し、operator()を実装します。
以下がbinary_functionの定義です。



binary_function基本クラスの定義

template<class _Arg1, class _Arg2, class _Result>
struct binary_function
{
   typedef _Arg1 first_argument_type;
   typedef _Arg2 second_argument_type;
   typedef _Result result_type;
};



binary_functionの定義を踏まえてオリジナルの二項関数オブジェクトを作成してみます。




例3

template <class T>
struct avgpoint : public binary_function<T, T, T>
{
   result_type operator()(first_argument_type first,
                                  second_argument_type second)
   {
       return (result_type) ((first-second) / 2) + second;
   }
};



二項関数オブジェクトavgpointは第一パラメータと第二パラメータの平均を求め戻り値として返しています。
したがって第一、第二、第三の各パラメータは同じclass Tで指定しています。
また、result_type・first_argument_type・second_argument_typeは基本クラスbinary_functionで定義されているのでそのまま使用しています。

それでは独自二項関数オブジェクトavgpointをSTLのアルゴリズムtransformに適用してみます。
アルゴリズムtransformは以下のフォーマットを持ちます。



template <class _InIt1, class _InIt2, class _OutIt, class _Fn2>
_OutIt transform(_InIt1 _First1, _InIt1 _Last1,
                  _InIt2 _First2, _OutIt _Dest,
                  _Fn2 _Func)



アルゴリズムtransformの第一パラメータと第三パラメータが指し示すイテレータを第五パラメータの関数オブジェクトに渡し、その結果を第四パラメータのイテレータに入れます。
その後、第一・第三・第四パラメータをインクリメントし、第一パラメータが第二パラメータになれば終了します。
関数オブジェクトavgpointをアルゴリズムtransformの第五パラメータへ渡すと第一・第三パラメータの平均値が第四パラメータへ格納されることになります。
以下にサンプルソースを示します。



例4

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

template <class T>
struct avgpoint : public binary_function<T,T,T>
{
   result_type operator()(first_argument_typefirst,
                                  second_argument_type second)
   {
       return (result_type) ((first-second) / 2) + second;
   }
};

int main(int argc, char* argv[])
{
   vector<double> v1(5), v2(5), v3(5);
   int i;

   v1[0] = 10.0;
   v1[1] = 98.6;
   v1[2] = 12.23;
   v1[3] = 88.8;
   v1[4] = -212.01;

   v2[0] = 2.0;
   v2[1] = 3.3;
   v2[2] = 4.19;
   v2[3] = 155.0;
   v2[4] = -2.0;

   cout << "Values in v1:?n";
   for(i=0; i<v1.size(); i++)
       cout << v1[i] << " ";
   cout << endl;

   cout << "Values in v2:?n";
   for(i=0; i<v2.size(); i++)
       cout << v2[i] << " ";
   cout << endl;

   transform(v1.begin(), v1.end(), 
                 v2.begin(), v3.begin(), 
                 avgpoint<double>());

   cout << "AVG. : ";
   for(i=0; i<v3.size(); i++)
       cout << v3[i] << " ";
   cout << endl;

   return 0;
}



実行結果
Values in v1:
10 98.6 12.23 88.8 -212.01
Values in v2:
2 3.3 4.19 155 -2
AVG. : 6 50.95 8.21 121.9 -107.005



実行結果を見るとアルゴリズムtransformの第四パラメータに渡したv3に平均値が格納されていることがわかります。



プリディケート(述語)


関数オブジェクトにはプリディケートと呼ばれるものがあります。
プリディケートとはパラメータを1つ以上取り戻り値がbool型の関数オブジェクトです。
プリディケートはアルゴリズムからパラメータを受け取り処理を行います。
その結果をbool型としてアルゴリズムに返し、アルゴリズムはプリディケートから受け取ったbool型を元に処理します。

プリディケートを引数に受け取るアルゴリズムにcount_ifがあります。
以下がサンプルソースです。




例5

#include <functional>
#include <algorithm>

using namespace std;

template <class T>
struct count5 : public unary_function<T,bool>
{
   bool operator()(argument_type t)
   {
       return t == 5? true : false;
   }
};

int main(int argc, char* argv[])
{
   int iAry[] = { 3, 5, 1, 6, 3, 1 };
   int iNo;
   iNo = count_if( iAry, iAry + 6, count5<int>() );
   printf( "%d?n", iNo );

   return 0;
}




実行結果
1



プリディケートcount5はアルゴリズムcount_ifからイテレータ経由でパラメータを受け取ります。
プリディケートcount5内では値が5の場合trueを返しているのでアルゴリズムcount_ifはtrueをカウントし戻り値として返しているのです。
その為、実行結果に1が表示されます。
最終更新:2006年11月28日 00:56