I have come across a use case where std::mem_fn cannot do something that a hand-rolled wrapper function can. It comes up when the wrapper function is used on something that's not of the method's class, but a type implicitly convertible to it:
#include <functional>
struct A
{
};
struct B
{
B(A); // implicit conversion from A to B
void foo() const;
};
auto foo1 = std::mem_fn(&B::foo); // std::mem_fn
void foo2(const B& b) { b.foo(); } // hand-rolled wrapper
int main()
{
A a;
foo1(a); // doesn't work
foo2(a); // works fine
}
The compiler error for the call to foo1 is the following (with GCC 4.8):
In file included from test.cpp:1:0:
functional: In instantiation of '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::_M_call(_Tp&, const volatile void*, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]':
functional:608:42: required from '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::operator()(_Tp&, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]'
test.cpp:21:11: required from here
functional:586:13: error: no match for 'operator*' (operand type is 'A')
{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
^
Would it have been possible to implement std::mem_fn in such a way that this use case works just like it does with the hand-rolled wrapper?
It would be possible, yes, but it's not how the C++ standard specifies
mem_fn.The standard says that
foo1(a)callsINVOKE(&B::foo, a)where that is defined in [func.require] as:Your case fails to meet the conditions of the first bullet, because
ais not an object of classB, nor a reference to aBor a class derived fromB, so the second bullet applies and so it's equivalent to((*a).*f)()which isn't valid.It's defined this way to allow smart pointers to be used, e.g.
The definition of
INVOKE(which is also used bybind,function,asyncand other parts of the library that create call wrappers) means that when invoking a wrapped pointer-to-member, if the first argumentt1isn't aTthen it is assumed to be some kind of pointer and gets dereferenced. This means it works withstd::shared_ptrandstd::unique_ptrbut also with types thatstd::mem_fnknows nothing about, such asboost::shared_ptrandMyVeryOwnSmartPtr.To make your code work it would be possible to add extra cases to handle when
t1is not aTor a type derived fromT, butis_convertible<T>::valueis true, and to invokeT(t1).*f)(), but that would complicate the specification and might have undesirable consequences in some cases.Your "wrapper" will force the implicit conversion of its argument, but it can't handle smart pointers or rvalues of type
B, which are both supported bymem_fn. If you have a specific case where you want to convertAobjects toBto call the function, then just do that, the genericmem_fntemplate isn't suitable, but it is more flexible and generic and works in plenty of other situations.(N.B. the definition of
INVOKEis actually defective because it dereferencesstd::reference_wrapperobjects in the same way as it dereferences youraargument. I've proposed a fix at http://cplusplus.github.com/LWG/lwg-active.html#2219, but that doesn't affect your example.)