Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

result_of does not define type for mem_fn

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?

like image 854
phimuemue Avatar asked Feb 08 '17 12:02

phimuemue


1 Answers

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.

like image 134
Barry Avatar answered Nov 17 '22 04:11

Barry