c++syntaxlanguage-designpointer-to-member

Why ISO C++ forbid taking the address of a bound member function to form a pointer to member function?


Here is the error in GCC (which i have already fixed):

ISO C++ forbid taking the address of a bound member function to form a pointer to member function

This is the error line of my code:

threads[i] = std::thread(&runnable->runTask, runnable, i, num_total_tasks);

runnable is an object and runTask is a non-static member function. The compiler throw me the error just the title.

I know non-static member function needs its object's pointer to be the first argument, but ISO C++ banned this way to form a pointer to member function. They recommend use &IRunnable::runTask. I use it to pass the compiler.

I really want to know that runnable->runTask has already given the infomation Who's the object and which function you use , why i need input the argument obj's pointer and use weird &IRunnable::runTask to reference a non-static member function.

One possible forbid reason for me is runtime Polymorphism, i think maybe C++ can't directly get what function you reference just by object pointer type, in the runtime it will decide to run the member function by the object's type the pointer really point to. So the address will change dynamically.

I'm a junior student in CS, so my thought may very naive. I want to get answers on a design perspective, thanks!


Solution

  • Demonstration

    Here is a version of the code that demonstrates the problem.

    #include <iostream>
    #include <thread>
    
    class foo {
    public:
       void bar() {
          std::cout << "Thread 3 executing\n";
          std::this_thread::sleep_for(std::chrono::milliseconds(10));
       }
    };
    
    auto main() -> int {
       foo f;
    
       std::thread t5(&foo::bar, &f); // runs properly
       t5.join();
    
       foo* fp{&f};
       std::thread t5x(&fp->bar, f);  // causes error
    }
    

    It is on Compiler Explorer.

    GCC 14.2 and CLang 19 generate different errors.

    Clang:

            <source>:18:22: error: member reference type 'foo' is not a pointer; did you mean to use '.'?
       18 |    std::thread t5x(&f->bar, f);
          |                     ~^~
          | 
            <source>:19:20: error: cannot create a non-constant pointer to member function
           19 |    std::thread t5x(&fp->bar, f);
              |  
    
                  ^~~~~~~~
    

    GCC:

    <source>:19:25: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function.  Say '&foo::bar' [-fpermissive]
    19 |    std::thread t5x(&fp->bar, f);
       |                     ~~~~^~~
    

    Explanation

    A member function pointer is not simply an address. It is a special value (a special prvalue) that can only be used as the left-hand operand of a member function call operator (->*), which returns a Callable. The OP is trying to pass it as a value, which isn't allowed.

    The thread call, and any invokable call, requires the address of the member (&foo::bar) and the object (this) pointer (&f). They combine them to create f->*bar(). The member access operator ('->*) is needed to pass this* to the function.