9

我有以下代码:

#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
}

第一次调用Apply编译正常,但如果我取消注释第二次调用,我会收到以下编译错误:

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) {
                          ^

我以某种方式期望这两个调用可以互换使用,并且std::mem_fn“会做正确的事情”。谁能解释一下,这里发生了什么?

4

1 回答 1

7

问题在这里:

int get() const& {
//            ^^^

您的成员函数是左值引用限定的。在你的Apply()

template<typename Func>
std::result_of_t<Func(X)> Apply(Func fn) {
    return fn(X{});
}

您正在使用右值调用它。这给我们带来了这两种表达方式之间的[让我感到非常惊讶的]差异:

X{}.get();        // ok
(X{}.*&X::get)(); // ill-formed

特定的指向成员的操作符上,成员指针的 ref 限定符会根据对象的值类别进行检查。来自 [expr.mptr.oper]:

.*对象表达式为右值的表达式中,如果第二个操作数是指向具有ref-qualifier &的成员函数的指针,则程序格式错误。在.*对象表达式为左值的表达式中,如果第二个操作数是指向具有 ref-qualifier 的成员函数的指针,则程序格式错误&&

所以第一个表达式是好的,get()const&-qualified 但右值可以绑定到它。第二个表达式不行——规则只是明确禁止它。

因此,您看到的行为是完全正确的 -mem_fn是通过直接调用成员函数来定义的,该函数在右值上格式不正确,因此Apply从重载集中删除。如果不是,那么实例化主体将是一个硬错误。

lambda 工作的原因是临时X绑定到 lambda 的引用参数。get()然后在左值函数参数上调用 - 而不是在传递给它的临时参数上调用。但即使没有这个,直接调用get()临时对象仍然可以。

于 2017-02-08T13:03:50.227 回答