1. ホーム
  2. c++

[解決済み] C++のファンクターとその使い方とは?

2022-03-19 15:25:13

質問

C++でファンクターについてよく聞きます。どなたか、ファンクターとは何か、どのような場合に有用か、概要を教えていただけませんか?

どのように解決するのですか?

ファンクタとは、要するにoperator()を定義したクラスです。これにより、関数のように見えるオブジェクトを作成することができます。

// this is a functor
struct add_x {
  add_x(int val) : x(val) {}  // Constructor
  int operator()(int y) const { return x + y; }

private:
  int x;
};

// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 
assert(out[i] == in[i] + 1); // for all i

ファンクターにはいくつか良い点があります。ひとつは、通常の関数と違って、状態を含むことができることです。上の例では、あなたが与えたものに42を足す関数を作っています。しかし、この42という値はハードコードされたものではなく、ファンクタのインスタンスを作成したときにコンストラクタの引数として指定されたものです。コンストラクタを別の値で呼び出すだけで、27を足す別の加算器を作ることができます。これによって、うまくカスタマイズできるのです。

最後の行にあるように、std::transform や他の標準ライブラリのアルゴリズムなど、他の関数への引数としてファンクタを渡すことはよくあります。普通の関数ポインタでも同じことができますが、上で述べたように、ファンクタは状態を含むので、より柔軟に "カスタマイズすることができます(もし私が関数ポインタを使いたかったら、その引数に正確に1を加える関数を書かなければなりません。ファンクタは一般的で、初期化したものを何でも追加します)、また、より効率的である可能性もあります。上の例では、コンパイラはどの関数が std::transform を呼び出す必要があります。呼び出すべきは add_x::operator() . つまり、その関数呼び出しをインライン化することができるのです。これによって、ベクトルの各値に対して手動で関数を呼び出した場合と同じように効率的になります。

もし、関数ポインターを渡したとしたら、コンパイラはそれがどの関数を指しているのかすぐにわからないから、かなり複雑なグローバル最適化をしない限り、実行時にポインターの参照を解除してから呼び出さなければならない。