【问题标题】:result_of does not define type for mem_fnresult_of 没有为 mem_fn 定义类型
【发布时间】:2017-06-26 02:41:35
【问题描述】:

我有以下代码:

#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 只是“会做正确的事情”。谁能解释一下,这里发生了什么?

【问题讨论】:

    标签: c++ c++14 member-function-pointers result-of


    【解决方案1】:

    问题出在这里:

    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
    

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

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

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

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

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

    【讨论】:

    • const &amp; 绑定到右值。
    • @Quentin 不完全是。 X().get() 编译。
    • 这里的问题是 [expr.mptr.oper]/6 在指向成员函数的指针上有 ref-qualifier 无条件限制左操作数的值类别,与X().get() 的普通重载解析不同,它使用标准引用绑定规则。
    • @T.C.所以X{}.get() 可以,但是(X{}.*&amp;X::get)() 不行吗?为什么? (我的意思是,我看到了规则,但为什么要区分呢?)
    • @phimeumue 没有type,因为调用表达式的格式不正确,并且由于成员指针的规则,它的格式不正确。如果您将 result_of_t 更改为 auto,您会看到更直接的错误。
    猜你喜欢
    • 2019-04-20
    • 1970-01-01
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 2020-09-23
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多