web-dev-qa-db-ja.com

なぜmem_fnを使用するのですか?

なぜstd::mem_fnが必要です。

呼び出し可能な関数(ラムダ、関数ポインターなど)を取り込んで、それを引数にバインドする関数があります。

例えば:

template<class T>
void Class::DoBinding(T callable) {
  m_callable = std::bind(callable, _1, 4);
}
//somewhere else
Item item;
m_callable(item);

私が見たすべてのコードサンプルは次のことを行います。

//some defined member function
Item::Foo(int n);

DoBinding(std::mem_fn(&Item::Foo));

なぜそれは単純にできないのですか?

DoBinding(&Item::Foo);

後者はstd :: mem_fnを使用せずに呼び出すことができるようですが、なぜそれが必要なのですか?

13
Jonathan.

これは、UnaryFunctionまたはBinaryFunctionを予期するジェネリックコードが、通常の呼び出し構文で直接呼び出すためです。したがって、 _for_each_ のような任意のアルゴリズムを選択するには、次のように実装できます。

_template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
    for (; first != last; ++first) {
        f(*first); // <== N.B. f(*first)
    }
    return f;
}
_

_&Item::Foo_を指定してfor_each()を呼び出した場合、コードは_(&Item::Foo)(x)_を呼び出そうとします。これは、メンバーへのポインターについては_(x.*&Item::Foo)()_を記述する必要があるため、形式が正しくありません。 _mem_fn_が解決することを意図しているのは、構文上の違いです。_mem_fn_は、メンバーへのポインターの呼び出し構文を処理するため、メンバーへのポインター、関数、および関数オブジェクトですべてのアルゴリズムを使用できます。 for_each(v.begin(), v.end(), &Item::Foo)を持つことはできませんが、for_each(v.begin(), v.end(), mem_fn(&Item::Foo))を持つことはできます。

これは、std::bind()(および_std::thread_および_std::function_および...)でネイティブに正常に機能します。これらはすべて、メンバーへのポインターを個別に明示的に処理するためです。また、DoBinding()自体がstd::bind()を呼び出すため、この場合は_std::mem_fn_の理由はありません


そこ です この構文上の違いを取り除くための提案でした: P0312 。うまくいきませんでした。

18
Barry

これは通常、DoBinding(std::mem_fn(&Item::Foo))を作成する人が、DoBindingがメンバーポインタを直接取得できることを知らないために行われます。

注意:std::sort(..., &Item::Foo)failになります。これは、sortが値が直接呼び出し可能な関数オブジェクトであることを想定しているためです。そしてメンバーポインタそうではない。実際、C++標準ライブラリのほとんどすべてのアルゴリズムは、直接呼び出すことができる型の代わりにメンバーポインターを指定すると、失敗します。 DoBindingが機能するのは、メンバーポインター用に特別なオーバーロードがあるstd::bindを使用しているためです。 DoBindingの呼び出し元は、必ずしもあなたがそうしていることを知っているとは限りません。

テンプレートパラメータによって呼び出し可能オブジェクトを受け取るほとんどのコードは、メンバーポインタでチョークします。したがって、安全のために、直接呼び出すことができるオブジェクトとしてメンバーポインタを渡さないでください。 mem_fnを使用して、そのようなオブジェクトに変換します。

11
Nicol Bolas