C ++成员函数指针定义

时间:2016-02-03 14:20:30

标签: c++ function pointers c++11

考虑以下代码:

#include <iostream>
#include <functional>

struct B {
    template <class C, class M, class T>
    void call1(C (M::*member)(), T *instance) {
        std::function<void()> fp = std::bind(member, instance);
        fp();
    }

    template <class C, class M, class T>
    void call2(C (M::*member), T *instance) {
        std::function<void()> fp = std::bind(member, instance);
        fp();
    }

    void foo() {
        call1(&B::func, this); // works
        call2(&B::func, this); // works

        call1(&B::func2, this); // Error: no matching member function for call to 'call2'
        call2(&B::func2, this); // works
    }

    void func() {
        std::cout << "func\n";
    }

    void func2() const volatile {
        std::cout << "func2\n";
    }
};

int main() {
    B{}.foo();
}

似乎后一版本不接受具有额外cv限定符的函数。

指定像C (M::*member)()这样的函数成员指针和这个C (M::*member)之间的区别是什么?

1 个答案:

答案 0 :(得分:15)

让我们简化为仅考虑以下之间的区别:

template <class C, class M> void f(C (M::*member)());
template <class C, class M> void g(C (M::*member));

f中,member是指向M成员函数的指针,返回零参数并返回C。如果您使用&B::func调用它,编译器将推断M == BC == void。简单。

g中,member只是指向M类型C成员的指针。但是,在我们的例子中,&B::func是一个函数。所以这里的影响就是放弃指针。我们再次推断M == B,而C变为void() - 现在C是一种函数类型。这是f不太专业的版本,因为它允许更多种类的成员。 g可以匹配带参数的函数,或者指向成员的指针,或相关的 cv - 合格的成员functinos。

让我们考虑一个重载函数的示例以及如何以不同方式推导它(这是您问题中的原始问题,已经编辑过,但仍然很有趣):

struct X {
    void bar() { }
    void bar(int ) { }
};

当我们这样做时:

f(&X::bar);

即使&X::bar是重载名称,但只有一个实际匹配C (M::*)()。有M == XC == void的人。 bar取代int的重载与模板类型无关。所以这是传递重载名称的可接受用途之一。这推断了罚款。

然而,当我们这样做时:

g(&X::bar);

现在,有两个完全虚假扣除。 C可以是void()void(int)。由于两者都是有效的,因此推论是模糊的,并且您无法编译 - 错误并不能使这一点特别清楚。

现在回到你的例子:

call1(&B::func2, this); // Error: no matching member function for call to 'call2'
call2(&B::func2, this); // works

&B::func2的类型为void (B::*)() const volatile。由于call1推断出一个不带args但不是cv-qualified的成员函数类型,所以类型推导就会失败。没有CM来匹配这些类型。

但是,call2扣除很好,因为它更通用。它可以匹配任何指向成员的指针。我们最终得到的是C = void() const volatile。这就是为什么这样做的原因。