I'm confused as to why std::mem_fn
is needed.
I have a function that takes in any callable (lambda, function pointer, etc),and binds it to an argument.
Eg:
template<class T>
void Class::DoBinding(T callable) {
m_callable = std::bind(callable, _1, 4);
}
//somewhere else
Item item;
m_callable(item);
All code samples I've seen do:
//some defined member function
Item::Foo(int n);
DoBinding(std::mem_fn(&Item::Foo));
Why can't it simply be:
DoBinding(&Item::Foo);
It seems the latter is callable without having to use std::mem_fn, so why is it needed?
This is because generic code that expects UnaryFunction
or BinaryFunction
will invoke it directly with the regular call syntax. So to pick an arbitrary algorithm like for_each
, it could well be implemented like:
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;
}
If you called for_each()
with &Item::Foo
, the code try to call (&Item::Foo)(x)
, which is ill-formed since for pointers to members you have to write (x.*&Item::Foo)()
. It's that syntactical difference that mem_fn
is meant to solve: mem_fn
deals with the invocation syntax of pointers to members so that you can use all the algorithms with pointers to members as well as functions and function objects. You cannot have for_each(v.begin(), v.end(), &Item::Foo)
but you can have for_each(v.begin(), v.end(), mem_fn(&Item::Foo))
.
This works just fine in std::bind()
(and std::thread
and std::function
and ...) natively since those all have explicit handling for pointers to members separately. And since DoBinding()
itself calls std::bind()
, there is no reason for std::mem_fn
in this case.
There is was a proposal to get rid of this syntactic difference: P0312. It did not go well.
This is usually done because the person who writes DoBinding(std::mem_fn(&Item::Foo))
has no idea that DoBinding
can take a member pointer directly.
Remember: std::sort(..., &Item::Foo)
will fail, because sort
expects the value to be a directly-callable function object. And member pointers are not. Indeed, pretty much every algorithm in the C++ standard library will fail when given a member pointer instead of a directly-callable type. Your DoBinding
only works because you're using std::bind
, which has a special overload for member pointers. The caller of DoBinding
doesn't necessarily know you're doing that.
Most code that takes callables by template parameters will choke on a member pointer. So to be safe, we don't pass member pointers around as objects that can be directly called; use mem_fn
to turn it into such an object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With