このコードの問題は何ですか?このコードは私に多くの構文エラーを与えています。また、C++でファンクターが使用される理由も知りたいです。
class f
{
public:
int operator(int a) {return a;}
} obj;
int main()
{
cout << obj(0) << endl;
}
このコードの問題は何ですか?このコードは私に多くの構文エラーを与えています。また、C++でファンクターが使用される理由も知りたいです。
class f
{
public:
int operator(int a) {return a;}
} obj;
int main()
{
cout << obj(0) << endl;
}
を宣言するときに、余分な括弧のペアがありませんoperator()
。関数の名前はですがoperator()
、その後もパラメーターのリストが必要です。したがって、次のようになります。
int operator()(int a) {return a;}
このような関数オブジェクト(別名ファンクター)は通常、関数へのポインターを使用する場合に使用されます。ただし、継承を使用でき、状態もカプセル化できるという利点があります。多くの場合、適切に設計されたクラスまたは関数テンプレートは、関数ポインターとほぼ互換的に使用できます。ただし、優れたオプティマイザは通常、テンプレートオブジェクトを使用すると、より優れたコードを生成できます。
関数オブジェクトの使用方法のかなり洗練された例については、式テンプレートを参照してください。
これは、継承を使用する方法の小さな、やや不自然な例です。
struct unary_int_func {
virtual int operator()(int i) = 0;
};
struct negate : public unary_int_func {
int operator()(int i) {return -i;}
};
struct one_plus : public unary_int_func {
int operator()(int i) {return i+1;}
};
void show_it(unary_int_func &op, int v) {
cout << op(v) << endl;
}
この場合、演算子を純粋仮想関数として使用して基本クラスを作成します。次に、それを実装する具象クラスを導き出します。などのコードshow_it()
は、このベースから派生したクラスの任意のインスタンスを使用できます。intを取り、intを返す関数へのポインタを使用することもできますが、これはよりタイプセーフです。関数ポインターを使用するコードは、そのような関数ポインターを受け入れますが、このようにして、intをintにマップするまったく新しい階層を定義できます。
struct a_different_base_class {
virtual int operator()(int i) = 0;
};
ただし、これのインスタンスはunary_int_funcのインスタンスと互換性がありません。
状態については、実行中の合計関数を検討してください。
struct running_sum : public unary_int_func {
int total;
running_sum() : total(0) {}
int operator()(int i) {return total += i;}
};
int main()
{
running_sum s;
cout << s(1) << endl;
cout << s(2) << endl;
cout << s(3) << endl;
cout << s(4) << endl;
}
ここで、のインスタンスはrunning_sum
合計を追跡します。1、3、6、および10が出力されます。関数へのポインターには、個別の呼び出し間で状態を保持するそのような方法はありません。関数オブジェクトに関するSGIのSTLページには、私の実行中の合計と同様の例がありますが、コンテナー内のさまざまな要素に簡単に適用する方法を示しています。
int operator(int)
事実上intに等しいからですint #something_missing_here#(int)
operator
は予約済みのキーワードであり、単独で使用する場合は有効な関数識別子/名前としての修飾子ではありません。
無効な識別子が使用されているにもかかわらず、指定された式が関数宣言であることをコンパイラーに理解させるために使用されます(C ++では、命名の最初の文字としてアルファベットとアンダースコアのみが許可されます)
ファンクターは基本的に状態のある関数です。それらの最大の使用法は、STLおよびBoostライブラリです。たとえば、std :: sortは、と呼ばれるタイプのファンクターを取りますComparator
。このコンテキストでは、代わりに関数オブジェクトを渡すこともできますが、ファンクターは、同じファンクターへの後続の呼び出しで使用および操作できるデータメンバーによって、より柔軟性を提供します。ファンクターは、C++コールバックを実装するためにも使用されます。
演算子のオーバーロードコードで問題をすでに理解しているので、ファンクターに関する疑問に対処したいと思います。
ファンクターは「関数ポインター」の略です。これらは、アルゴリズムの動作をカスタマイズするためのハンドルを提供するために広く使用されています。たとえば、STLの並べ替えアルゴリズムは、パラメーターとしてファンクターを使用し、ユーザー(プログラマー)は、2つの要素の比較結果をアルゴリズムに通知する関数を実装できます。
functorは、通常、operator()をオーバーロードするオブジェクト(クラスまたは構造体のインスタンス)です。ファンクターと通常の関数の違いは、ファンクターはオブジェクトであるため、呼び出し間で状態を維持できることです。
ファンクターはオブジェクトであるため、継承のルールも適用され、これを有利に使用できます。
STLを使用する場合にも、ファンクターが役立ちます。std :: sort、std :: for_eachなどを使用すると、コンテナ全体(配列を含む)のコンテンツを処理できます。これがcplusplus.comからの例です:
// for_each example
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void myfunction (int i) {
cout << " " << i;
}
struct myclass {
void operator() (int i) {cout << " " << i;}
} myobject;
int main () {
vector<int> myvector;
myvector.push_back(10);
myvector.push_back(20);
myvector.push_back(30);
cout << "myvector contains:";
for_each (myvector.begin(), myvector.end(), myfunction);
// or:
cout << "\nmyvector contains:";
for_each (myvector.begin(), myvector.end(), myobject);
cout << endl;
return 0;
}
これを試して:
class f
{
public:
int operator(int a) {return a;}
};
int main()
{
f obj;
cout<<obj(0)<<endl;
}