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.
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