为什么我不能在绑定中使用 mem_fn 仿函数?

Why Can't I use a mem_fn Functor in bind?

我想将 mem_fn 参数传递给 bind,但编译器似乎不允许。

例如,这很好用:

accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, bind(&foo::r, placeholders::_2)));

但是当我尝试使用 mem_fn 仿函数时,我遇到了一页错误:

accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, mem_fn(&foo::r)));

/usr/include/c++/6/bits/stl_numeric.h: In instantiation of ‘_Tp std::accumulate(_InputIterator, _InputIterator, _Tp, _BinaryOperation) [with _InputIterator = __gnu_cxx::__normal_iterator >; _Tp = int; _BinaryOperation = std::_Bind(std::_Placeholder<1>, std::_Mem_fn)>]’:
prog.cpp:20:102: required from here
/usr/include/c++/6/bits/stl_numeric.h:154:22: error: no match for call to ‘(std::_Bind(std::_Placeholder<1>, std::_Mem_fn)>) (int&, foo* const&)’

嗯,很明显,第二个例子没有提到placeholders::_2。当 accumulate 使用两个参数调用仿函数时,第二个参数将被忽略,并且您的代码试图将 intmem_fn 的内部 class 的实例相加returns.

我建议你放弃所有这些 bind 游戏,并使用 lambda:

accumulate(cbegin(foos), cend(foos), 0, 
    [](int val, foo* f) { return val + f->r(); });

这里发生的事情更清楚了。

要理解这一点,想一想如果您只是将文字传递给 bind 的 3rd 参数意味着什么。例如,如果您完成了:

accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, 13))

结果应该是 size(foos) * 13,因为 plus 会使用 13 作为每次迭代的加数。

accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, mem_fn(&foo::r)))

不会编译,因为它试图将 mem_fn(&foo::r) 的结果作为加数传递给 plus。由于无法转换为 int plus 无法接受。但即使它可以转换为 int,这也不是您要查找的内容,您想要采用 2nd 参数并调用 foo::r它,将结果传递给 plus。因此我们知道我们需要看到,placeholders::_2 在语句中的某处使用,传达 2nd 参数用于调用它的 r 方法。


我们需要绑定 placeholders::_2 以绑定到一个函子,该函子将对其参数调用 r 方法。绑定当然需要 bind,但实际上 bind can take a method as it's 1st argument.

也就是说,您的工作代码中的 bind(&foo::r, placeholders::_2) 语句在非嵌套形式中没有任何意义;那个仿函数甚至不接受 2 个参数! actually has special rules for handling a bind nested within another bind,以便它们可以共享外部 bind 的占位符,以免无法将绑定参数传递给嵌套表达式:

If the stored argument arg is of type T for which std::is_bind_expression<T>::value == true (for example, another bind expression was passed directly into the initial call to bind), then bind performs function composition: instead of passing the function object that the bind subexpression would return, the subexpression is invoked eagerly, and its return value is passed to the outer invokable object. If the bind subexpression has any placeholder arguments, they are shared with the outer bind.


在此表达式中使用 mem_fn 的唯一方法是将其结果传递给 bind 以传达 placeholders::_2bind(mem_fn(&foo::r), placeholders::_2) 这可行,但是当简单的 bind(&foo::r, placeholders::_2) 就足够时,这是一个不必要的步骤。因此,生成此仿函数的最佳方法是使用您提供的语句:

accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, bind(&foo::r, placeholders::_2)))

或者使用 lambda:

accumulate(cbegin(foos), cend(foos), 0, [](const int augend, const auto& addend) { return augend + addend.r(); } )