出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2020/10/03 01:18 UTC 版)
「関数オブジェクト」の記事における「C++ での関数オブジェクト」の解説
C++では、クラスあるいは構造体において関数呼び出し演算子の多重定義が可能となっている。具体的には、任意の引数を持つoperator()メンバー関数を定義することで、そのオブジェクトのインスタンスを指す変数の名前が、あたかも関数名(関数指示子)であるかのような構文で、定義した関数を呼ぶことができる。このような C++ のオブジェクトを、C++ の用語ではファンクタ (functor) と呼ぶ(#C++のファンクタとファンクショノイド を参照)。 struct my_adder { int operator()(int a, int b) const { return a + b; }};my_adder f;int result = f(2, 3);// 以下のように呼び出すことも可能だが、通例使われない。f.operator()(2, 3); C++のclass/structは、デフォルトのアクセスレベル(アクセス修飾子(英語版))がprivate/publicであるという違いしかないため、関数オブジェクトの記述ではclassの代わりにstructが使われることも多い。 配列の中から特定の条件に該当する要素の個数を数えるルーチンの例を考えてみよう。ただし条件の判定処理は自由にカスタマイズできるものとする。 関数へのポインタを使用する C のプログラムは、たとえば下記のようになる。 #include /* 引数が条件に該当する場合は 1 を、該当しない場合は 0 を返す */typedef int PredicateFunc(int x);size_t countIf(const int data[], size_t num, PredicateFunc* predicate) { size_t count = 0; size_t i; for (i = 0; i < num; ++i) { if (predicate(data[i])) { count++; } } return count;}int isPositive(int x) { return x > 0;}int isEven(int x) { return x % 2 == 0;}int main(void) { const int data[] = { 0, 1, 2, 3, 4, 5, 6, 0, -1, -2, -3, -4 }; printf("Count of positive numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), &isPositive)); printf("Count of even numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), &isEven));} 一方、C++の関数オブジェクトを利用すると下記のようになる。 #include template size_t countIf(const int data[], size_t num, TPredicate predicate) { size_t count = 0; for (size_t i = 0; i < num; ++i) { if (predicate(data[i])) { count++; } } return count;}struct IsPositiveFunctor { bool operator()(int x) const { return x > 0; }};struct IsEvenFunctor { bool operator()(int x) const { return x % 2 == 0; }};int main() { const int data[] = { 0, 1, 2, 3, 4, 5, 6, 0, -1, -2, -3, -4 }; std::printf("Count of positive numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), IsPositiveFunctor())); std::printf("Count of even numbers = %ld\n", (long)countIf(data, sizeof(data) / sizeof(*data), IsEvenFunctor()));} コールバックを述語 (predicate) として countIf() 関数テンプレートに渡す際、関数へのポインタではなく構造体のインスタンス(関数オブジェクト)を渡していることに注意されたい。 上記の例において関数オブジェクトによるカスタマイズを可能にしているのは、テンプレートと関数呼び出し演算子のオーバーロードによる静的ダック・タイピングである。関数テンプレートを実体化する際の字句解析に適合しさえすれば、述語にはどんなオブジェクトでも渡せる。 コールバック関数が実行されると、他のメンバー関数と同様に働き、すなわちオブジェクトの他のメンバー(データや関数)に対して完全にアクセスすることができる。 クラス型の関数オブジェクトに加えて、C++ では別の種類の関数オブジェクトが可能である。[要校閲] C++ のメンバーポインタや、テンプレート機能を利用することができ、テンプレートの記述力により、(関数の合成などの)別種の関数オブジェクトを定義するといったいくつかの関数型言語の技法を用いることができる。[要校閲] C++ の Standard Template Library (STL) では、テンプレートの述語 (predicate) として関数オブジェクトを多用している。
※この「C++ での関数オブジェクト」の解説は、「関数オブジェクト」の解説の一部です。
「C++ での関数オブジェクト」を含む「関数オブジェクト」の記事については、「関数オブジェクト」の概要を参照ください。