Why use mem_fn?

I am confused why std::mem_fn .

I have a function that takes any called (lambda, function pointer, etc.) and binds it to an argument.

For instance:

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

All the code examples I've seen are:

 //some defined member function Item::Foo(int n); DoBinding(std::mem_fn(&Item::Foo)); 

Why it can't be easy:

 DoBinding(&Item::Foo); 

It seems that the latter can be called without using std :: mem_fn, so why is it needed?

+5
source share
2 answers

This is because generic code that expects a UnaryFunction or BinaryFunction is called directly using regular call syntax. Therefore, to select an arbitrary algorithm, such as for_each , it could be implemented as follows:

 template<class InputIt, class UnaryFunction> UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f) { for (; first != last; ++first) { f(*first); // <== NB f(*first) } return f; } 

If you called for_each() with &Item::Foo , the code tries to call (&Item::Foo)(x) , which is poorly formed, because for member pointers you need to write (x.*&Item::Foo)() . This means that the syntax difference mem_fn to solve is: mem_fn deals with the syntax for calling member pointers so that you can use all the algorithms with member pointers, 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 fine in std::bind() (and std::thread and std::function and ...) initially, since they all have explicit processing of pointers to elements separately. And since DoBinding() itself calls std::bind() , in this case there is no reason for std::mem_fn .


There is a suggestion to get rid of this syntax difference: P0312 .

+10
source

This is usually done because the person who writes DoBinding(std::mem_fn(&Item::Foo)) does not know that DoBinding can directly accept a pointer to a member.

Remember: std::sort(..., &Item::Foo) will fail because sort expects this value to be the function calling object. And member pointers are not. In fact, almost every algorithm in the C ++ standard library crashes when using a member pointer instead of a type with a direct call. Your DoBinding only works because you use std::bind , which has a special overload for member pointers. The calling DoBinding does not necessarily know that you are doing this.

In most cases, code that accepts calls by template parameters will overwhelm a pointer to an element. Therefore, to be safe, we do not pass element pointers as objects that can be called directly; use mem_fn to turn it into such an object.

+8
source

All Articles