私は概念の初心者ですが、違いを検索すると、ファンクターの利点は、内部に値を格納し、構築からこれらの値を初期化できることですが、すべての引数を全体として取ることを除いて、通常の関数も同じように機能します関数呼び出しで。ほとんどの場合、私は何らかの方法で間違っていますが、通常の関数に関連するファンクターのトリックと利点はどこにありますか
2 に答える
主な違いは、ファンクターが関数ではなく型を定義することです。ステートレス ファンクタ (添付データなし) でさえ、これを利用できます。たとえばstd::less
、ソートアルゴリズム内での使用を検討してください。
template <typename Iterator, typename Comparator>
sort(Iterator begin, Iterator end, Comparator c) {
...
if (c(*begin,*end)) { ...
...
}
として呼び出されsort(v.begin(), v.end(), std::less<int>());
ます。関数が呼び出されると、 のインスタンスstd::less<int>
が作成され、テンプレートに渡されます。ステートレスであるため、関数を渡すコストはほとんどありません。関数内では、呼び出しc(a,b)
は への呼び出しであると判断されc.operator()(a,b)
、コンパイラは型を認識します。呼び出しを効率的にインライン化し (この場合は十分に単純です)、単一の比較命令で置き換えることができます。
一方、同等の C 関数qsort
は関数ポインターを取ります (関数を値で渡すことはできません)。内部qsort
では、コンパイラは呼び出された関数が何であるかを認識せず、インライン化できないため、比較ごとに関数呼び出しを実行する必要があります。
ファンクターは、呼び出しの場所で後で使用できる追加情報を追加する (これは単純な関数では不可能です) ことと、呼び出す必要があるものなどの情報を提供するなどの追加情報を渡すことの両方に役立ちます (同じ動作を取得できますが、パフォーマンスに影響を与える) またはその他の添付情報 (型にはネストされた型/typedef、特性検査のための情報を含めることができます...)
通常の関数、独立型またはメンバーは、関数が呼び出されたときに渡される引数のみを持ちます。そのため、追加のデータを関数に渡す方法はありません。
これはファンクタとは異なります。ファンクターはオブジェクトのインスタンスであり、コンストラクター (ファンクターを渡すときに使用する) に渡されたデータを実際に格納できます。
C++ 11 では、ラムダはキャプチャを使用して (技術的に正しい単語ではない) 値を「格納」することもできるため、少し混乱します。またはstd::bind
、呼び出し可能なオブジェクトが実際に呼び出されたときに、引数として値をバインドできるようにする which を使用することによって。