I wanted to pass a mem_fn
argument to bind
but the compiler doesn't seem to allow it.
For example this works fine:
accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, bind(&foo::r, placeholders::_2)));
But when I try to use the mem_fn
functor I get about a page of errors:
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&)’
To understand this, think about what it would mean if you just passed a literal to bind
's 3rd argument. For Example if you had done:
accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, 13))
The result would have been size(foos) * 13
, because plus
would have used 13
as it's addend on each iteration.
accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, mem_fn(&foo::r)))
Won't compile because it's attempting to pass the result of mem_fn(&foo::r)
as the addend to plus
. Since that can't be converted to an int
plus
can't accept that. But even if it could be converted to an int
, that's not what you're looking for, you want to take the 2nd argument and call foo::r
on it, passing the result to plus
. Thus we know we need to see, placeholders::_2
used somewhere in the statement, conveying the 2nd argument for the calling of it's r
method.
We need to bind placeholders::_2
to be bound to a functor which will call the r
method on it's parameter. The binding will of course require bind
, but actually bind
can take a method as it's 1st argument.
That said, the bind(&foo::r, placeholders::_2)
statement from your working code doesn't make any sense in non-nested form; that functor doesn't even take 2 parameters! c++ actually has special rules for handling a bind
nested within another bind
, so that they can share the outer bind
's placeholders, lest there be no way to convey a bound argument to a nested expression:
If the stored argument arg is of type
T
for whichstd::is_bind_expression<T>::value == true
(for example, anotherbind
expression was passed directly into the initial call tobind
), thenbind
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 thebind
subexpression has any placeholder arguments, they are shared with the outerbind
.
The only way to use mem_fn
in this expression would be to pass it it's result to bind
for the conveying of placeholders::_2
: bind(mem_fn(&foo::r), placeholders::_2)
This works, but is an unnecessary step when the simple bind(&foo::r, placeholders::_2)
will suffice. Thus the best way to generate this functor is either with your proffered statement:
accumulate(cbegin(foos), cend(foos), 0, bind(plus<int>(), placeholders::_1, bind(&foo::r, placeholders::_2)))
Or by using a lambda:
accumulate(cbegin(foos), cend(foos), 0, [](const int augend, const auto& addend) { return augend + addend.r(); } )