STLアルゴリズムで使用する述語関数を記述しようとしています。述語を定義する方法は2つあると思います。
(1)以下の簡単な関数を使用します。
bool isEven(unsigned int i)
{ return (i%2 == 0); }
std::find_if(itBegin, itEnd, isEven);
(2)operator()関数を次のように使用します。
class checker {
public:
bool operator()(unsigned int i)
{ return (i%2 == 0); }
};
std::find_if(itBegin, itEnd, checker);
私は通常、いくつかのメンバーを含む述語オブジェクトを作成し、それらをアルゴリズムで使用するため、2番目のタイプをさらに使用します。チェッカー内に同じisEven関数を追加して述語として使用すると、エラーが発生します。
3。エラーを与える構文:
class checker {
public:
bool isEven(unsigned int i)
{ return (i%2 == 0); }
};
checker c;
std::find_if(itBegin, itEnd, c.isEven);
コンパイル中にc.isEvenを呼び出すと、関数への未定義の参照を示すエラーが発生します。 3.がエラーを出している理由を誰かが説明できますか?また、私は述語とイテレータの基本について読むためのポインタをいただければ幸いです。
c.isEven()
の型が
_bool (checker::*)(unsigned int) // member function of class
_
これはfind_if()
では予期されない可能性があります。 _std::find_if
_には、関数ポインター(bool (*)(unsigned int)
)または関数オブジェクトのいずれかが必要です。
編集:別の制約:非static
メンバー関数ポインターはclass
オブジェクトによって呼び出される必要があります。あなたの場合、たとえメンバー関数を渡すことに成功したとしても、find_if()
はchecker
オブジェクトに関する情報を持ちません。そのため、メンバー関数のポインター引数を受け入れるためにfind_if()
をオーバーロードしても意味がありません。
注:一般的に_c.isEven
_は、メンバー関数ポインターを渡す正しい方法ではありません。 _&checker::isEven
_として渡す必要があります。
メンバー関数へのポインターにはインスタンスを呼び出す必要があり、メンバー関数ポインターのみを_std::find_if
_に渡します(実際には構文が正しくないため、まったく機能しません。正しい構文はstd::find_if(itBegin, itEnd, &checker::isEven)
でも、私が指定した理由でまだ機能しません)。
_find_if
_関数は、単一のパラメーター(テストするオブジェクト)を使用して関数を呼び出すことができると想定していますが、メンバー関数を呼び出すには、インスタンスthis
ポインターと比較するオブジェクトの2つが実際に必要です。
operator()
をオーバーロードすると、インスタンスと関数オブジェクトの両方を同時に渡すことができます。これは、これらのオブジェクトが同じになったためです。メンバー関数ポインターを使用して、1つだけを期待する関数に2つの情報を渡す必要があります。
_std::bind
_(_<functional>
_ヘッダーが必要)を使用してこれを行う方法があります。
_checker c;
std::find_if(itBegin, itEnd, std::bind(&checker::isEven, &c, std::placeholders::_1));
_
コンパイラが_std::bind
_をサポートしていない場合は、_boost::bind
_を使用することもできます。ただし、operator()
をオーバーロードするだけでは、これを行う利点はありません。
もう少し詳しく説明すると、_std::find_if
_は、シグネチャbool (*pred)(unsigned int)
に一致する関数ポインタまたはそのように動作するものを想定しています。述語の型はテンプレートによってバインドされるため、実際には関数ポインタである必要はありません。 bool (*pred)(unsigned int)
のように動作するものはすべて受け入れ可能です。これがファンクタが機能する理由です。これらは単一のパラメータで呼び出され、bool
を返すことができます。
他の人が指摘したように、_checker::isEven
_の型はbool (checker::*pred)(unsigned int)
であり、呼び出されるにはchecker
のインスタンスが必要なため、元の関数ポインターのように動作しません。
メンバー関数へのポインターは、概念的には、追加の引数、this
ポインター(たとえば、bool (*pred)(checker*, unsigned int)
)を取る通常の関数ポインターと見なすことができます。 std::mem_fn(&checker::isEven)
を使用して、そのように呼び出すことができるラッパーを実際に生成できます(これも_<functional>
_から)。 _std::find_if
_がまだ好まない、1つだけではなく2つのパラメーターで呼び出す必要がある関数オブジェクトがあるため、これはまだ役に立ちません。
_std::bind
_を使用すると、メンバー関数へのポインターが、this
ポインターを最初の引数として取る関数であるかのように扱われます。 _std::bind
_に渡される引数は、最初の引数が常に_&c
_であり、2番目の引数が新しく返される関数オブジェクトの最初の引数にバインドされることを指定します。この関数オブジェクトは、1つの引数で呼び出すことができるラッパーであるため、_std::find_if
_で使用できます。
_std::bind
_の戻り値の型は指定されていませんが、バインドされた関数オブジェクトを別の関数に直接渡すのではなく明示的に参照する必要がある場合は、std::function<bool(unsigned int)>
(この特定の場合)に変換できます。私の例でやったように。
_checker::isEven
_は関数ではありません。メンバー関数です。また、checker
オブジェクトへの参照がなければ、非静的メンバー関数を呼び出すことはできません。したがって、関数ポインターを渡すことができる古い場所でメンバー関数を使用することはできません。メンバーポインターには特別な構文があり、呼び出すには_()
_以外のものも必要です。
ファンクタがoperator()
を使用するのはそのためです。これにより、メンバー関数ポインターを使用せずにオブジェクトを呼び出すことができます。
私は functors (関数オブジェクト)を好みます。これは、プログラムを読みやすくし、さらに重要なことに、意図を明確に表現するためです。
これは私のお気に入りの例です:
template <typename N>
struct multiplies
{
N operator() (const N& x, const N& y) { return x * y; }
};
vector<int> nums{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Example accumulate with transparent operator functor
double result = accumulate(cbegin(nums), cend(nums), 1.1, multiplies<>());
注:近年は ラムダ式 がサポートされています。
// Same example with lambda expression
double result = accumulate(cbegin(nums), cend(nums), 1.1,
[](double x, double y) { return x * y; });
この例では、呼び出し演算子(operator()
)を使用する必要があるとしていますが、この例では関数をisEven
と呼びます。次のように書き直してください。
class checker {
public:
bool operator()(unsigned int i)
{ return (i%2 == 0); }
};