関数オブジェクト(ファンクタ)

http://www.geocities.jp/ky_webid/cpp/library/015.html

関数オブジェクトのお話です。パッと見はややこしそうですが、なんとかなると思います。

まずは関数オブジェクトが単なる()演算子オーバーロードであることを理解した上で、実装してみます。

#include <iostream>
using namespace std;

class CSample {
public:
    void operator()(int num) {
        cout << num << endl;
    }
};

int main () {
    CSample obj;
    obj(100);
    
    return 0;
}
$ main
100

オブジェクトを関数のように括弧付きで呼び出すと()演算子オーバーロードの処理が呼び出されます。

元記事の方では関数オブジェクトの使用サンプルとしてstd::for_eachへ渡す処理を書いていますが、これは単に関数のポインタを渡すだけで良いような気がするんですがどうなんでしょうか。

一応試してみました。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void print_data(int num) {
    cout << num << endl;
}

int main () {
    vector<int> data;
    
    for(int i=0;i<5;++i) {
        data.push_back(i);
    }
    
    // 関数のポインタを渡す
    for_each(data.begin(),data.end(),print_data);
    
    return 0;
}
$ main
0
1
2
3
4

うまく動いていますね。

関数ポインタでも関数オブジェクトでもfor_eachの使い手は意識しなくても渡せるということなんでしょうか。これは便利ですね。

自分でもfor_eachと同じような物を実装してみました。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

template<class T,class U> void myfor_each(T it, T end, U data) {
    while (it != end) {
        data(*it);
        ++it;
    }
}

class CSample {
public:
    void operator()(int num) {
        cout << num << endl;
    }
};

void print_data(int num) {
    cout << num << endl;
}

int main () {
    vector<int> data;
    
    for(int i=0;i<5;++i) {
        data.push_back(i);
    }
    
    // 関数ポインタを渡す
    myfor_each(data.begin(),data.end(),print_data);
    
    // 関数オブジェクトを渡す
    myfor_each(data.begin(),data.end(),CSample());
    
    return 0;
}
$ main
0
1
2
3
4
0
1
2
3
4

うまく動いてますね。

ただ一つ気になったのですが、「U data」としてるだけなのに関数のポインタを受け取れてる部分ですね。

通常、関数のポインタを得るための宣言は「void (*data)(int)」のようにしなければならないはずで「U data」だけでも動くということはテンプレート展開されるときによきに計らってくれるということなんでしょうか。

そういえばテンプレートクラスにおいて、引数に関数のポインタを受け取る場合の書き方ってまだWebで勉強してた時には触れられたことなかったので上記の書き方であってるかちょっと不安です。

ロベール本の方もようやくクラスのところまで来たのでこのあたりの話が出てくるといいですね。