Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Member Function Pointer Definition

Consider this code:

#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();
}

It seems that the later version don't accept functions with extra cv qualifiers.

What is the difference between specifying a function member pointer like this C (M::*member)() and like this C (M::*member)?

like image 809
Crossfire Avatar asked Feb 03 '16 14:02

Crossfire


People also ask

What is pointer member function?

Pointers to members allow you to refer to nonstatic members of class objects. You cannot use a pointer to member to point to a static class member because the address of a static member is not associated with any particular object.

How do you define a pointer to a class member?

Defining a Pointer of Class type We can define pointer of class type, which can be used to point to class objects. Here you can see that we have declared a pointer of class type which points to class's object. We can access data members and member functions using pointer name with arrow -> symbol.

What is member function in C?

Member functions are operators and functions that are declared as members of a class. Member functions do not include operators and functions declared with the friend specifier. These are called friends of a class. You can declare a member function as static ; this is called a static member function.


1 Answers

Let's simplify to just considering the difference between:

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

In f, member is a pointer to a member function of M returning taking zero arguments and returning C. If you called it with &B::func, the compiler will deduce M == B and C == void. Straightforward.

In g, member is just a pointer to a member of M of type C. But, in our case, &B::func is a function. So the impact here is just dropping the pointer. We deduce M == B again, whereas C becomes void() - now C is a function type. This is a less specialized version of f in that it allows for more kinds of members. g can match against functions that take arguments, or against pointers to members, or, relevantly, against cv-qualified member functinos.

Let's consider as an example an overloaded function and how it would get deduced differently (this was the original problem in your question, which has since been edited, but is still interesting):

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

When we do:

f(&X::bar);

Even though &X::bar is an overloaded name, only one actually matches C (M::*)(). The one that has M == X and C == void. There is simply no way that the overload of bar taking an int matches the template type. So this is one of the acceptable uses of passing an overloaded name. This deduces fine.

However, when we do:

g(&X::bar);

Now, there are two perfectly vaild deductions. C could be both void() and void(int). Since both are valid, the deduction is ambiguous, and you fail to compile - with an error that doesn't really make this particularly clear.


Now back to your example:

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

The type of &B::func2 is void (B::*)() const volatile. Since call1 deduces on a member function type that takes no args and isn't cv-qualified, the type deduction simply fails. There is no C or M to get those types to match.

However, the call2 deduction is fine since it is more generic. It can match any pointer to member. We simply end up with C = void() const volatile. That's why this works.

like image 58
Barry Avatar answered Oct 15 '22 21:10

Barry