Home > Software design >  Pointer to a member function does not contain the information of the instance?
Pointer to a member function does not contain the information of the instance?

Time:10-21

(This is a question after reading this answer)

I tried this code:

#include <iostream>

class A {
public:
 int a;
 A(int val) : a{val}{}
 int f(int);
 int (A::*p)(int);
};

int A::f(int in) {
 return in   a;
}

int main() {
 A a1(1000);
 A a2(2000);
 
 // Without these two lines, segmentation fault.
 a1.p = &A::f; // no instance information.
 a2.p = &A::f; // no instance information.
 
 std::cout << a1.p << " " << a2.p << '\n';
 std::cout << std::boolalpha << (a1.p == a2.p) << '\n';
 
 std::cout << (a1.*(a1.p))(5) << '\n';
 std::cout << (a1.*(a2.p))(5) << '\n';
 std::cout << (a2.*(a1.p))(5) << '\n'; 
 std::cout << (a2.*(a2.p))(5) << std::endl;
}

Results:

1 1
true
1005
1005 // the same result as above.
2005
2005 // the same result as above.
  1. It seems that a1.p and a2.p are the same. Is the member function's address not assigned until it is dereferenced and called by attaching the instance's name?

  2. If so, what's the point of assigning &A::f, if it has no information about the instance?

CodePudding user response:

Non-static member functions have a "hidden" first argument that becomes the this pointer inside the function. So instance is not part of the function itself, but rather passed as an argument.

Note the quotes around "hidden", because it's only hidden in the function signature. When using other ways to call e member function, it's not really hidden but actually passed like any other argument. For example if using std::bind or std::thread, non-static member function are called with the instance as an actual argument.

Example:

struct thread_class
{
    int value_;

    explicit thread_class(int value)
        : value_{ value }
    {
    }

    void thread_func(int arg)
    {
        std::cout << "arg is " << arg << '\n';
        std::cout << "value is " << value_ << '\n';
    }
};

int main()
{
    thread_class foo;
    std::thread thread(&thread_class::thread_func, &foo, 123);
    thread.join();
}

The three arguments to the std::thread constructor is the function to run, and the arguments to the function itself. The first is the object to call the function on, what becomes this inside thread_func.

CodePudding user response:

It seems that a1.p and a2.p are the same. Is the member function's address not assigned until it is dereferenced and called by attaching the instance's name?

The address of a member function is independent of the instance on which it is invoked on. From class.mem:

The type of a non-static member function is an ordinary function type, and the type of a non-static data member is an ordinary object type. There are no special member function types or data member types.

(emphasis mine)

For the purposes of overload resolution, it is as if the non-static member function has an implicit object parameter of type X& if the member function is non-const and const X& if the member function is const. This implicit object parameter basically refers to the object on which it is invoked on.


Now coming to your second question:

If so, what's the point of assigning &A::f, if it has no information about the instance?

To identify the member member f with the pointer p. For example, there can be more than one member functions in a class. Like struct A{void f(); void g(int);int (A::*p)(int);};. So by writing a1.p = &A::f you're telling the compiler that you want the address of f to be assigned to a1.p. Note also that the type of a non-static data member is also an ordinary object type.

The information about the instance(like on which instance to call) will be provided when dereferencing the pointer p as you've done in (a1.*(a1.p))(5) and (a2.*(a1.p))(5).

  • Related