I have the following piece of code:
#include <functional>
struct X {
int get() const& {
return 42;
}
};
template<typename Func>
std::result_of_t<Func(X)> Apply(Func fn) {
X x;
return fn(x);
}
int main(void) {
Apply([](X const& x){return x.get();});
//Apply(std::mem_fn(&X::get)); // does not compile
}
The first call to Apply
compiles fine, but if I uncomment the second call, I get the following compilation error:
main.cpp:16:5: error: no matching function for call to 'Apply'
Apply(std::mem_fn(&X::get)); // does not compile
^~~~~
main.cpp:10:27: note: candidate template ignored: substitution failure [with Func = std::_Mem_fn<int (X::*)() const &>]: no type named 'type' in 'std::result_of<std::_Mem_fn<int (X::*)() const &> (X)>'
std::result_of_t<Func(X)> Apply(Func fn) {
^
I somehow expected that both calls could be used interchangeably and that std::mem_fn
just "would do the right thing". Can anybody explain, what happens here?
The problem is here:
int get() const& {
// ^^^
Your member function is lvalue-reference qualified. In your Apply()
:
template<typename Func>
std::result_of_t<Func(X)> Apply(Func fn) {
return fn(X{});
}
you're invoking it with an rvalue. Which brings us to the [very surprising to me] difference between these two expressions:
X{}.get(); // ok
(X{}.*&X::get)(); // ill-formed
On specifically pointer-to-member operators, the ref-qualifiers of the member pointer are checked against the value category of the object. From [expr.mptr.oper]:
In a
.*
expression whose object expression is an rvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier&
. In a.*
expression whose object expression is an lvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier&&
.
So the first expression is okay, get()
is const&
-qualified but rvalues can bind to that. The second expression is not okay - the rules just explicitly prohibit it.
So the behavior you see is perfectly correct - mem_fn
is defined by directly invoking the member function, which is ill-formed on an rvalue, so Apply
is removed from the overload set. If it were not, then instantiating the body would be a hard error.
The reason the lambda works is that the temporary X
is bound to the lambda's reference parameter. get()
is then invoked on the lvalue function parameter - not on the temporary passed into it. But even without that, invoking get()
directly on the temporary would still be fine.