jersey bean
jersey bean

Reputation: 3619

Appending ampersand to a class method

I came across some C++ code which looks roughly like this:

        void (classA::*methodA)() const noexcept
        {
            &classA::methodB
        };

Note: obviously I'm abstracting out the class name and method names in this example.

What is the purpose of methodA?

Analysis of the code

  1. It appears to be referencing methodB, but without parentheses, so it doesn't appear to be calling the method
  2. It uses ampersand & which reads to me as take the address of
  3. Lastly methodA doesn't return anything

Overall it appears to be just referencing the address of methodB.

Any suggestions as to what this is accomplishing?

Upvotes: 1

Views: 183

Answers (2)

metal
metal

Reputation: 6332

The key information is that you're initializing a pointer to a member function (PMF) using brace initializer syntax. See the C++ FAQs on PMFs.

Here's your code illustrated and commented, with an alternate way to write the same thing that is less confusing because it uses a type alias:

#include <iostream>

// Declare a class with a non-static method
class classA 
{
public:
    void methodB() const noexcept { std::cout << "methodB\n"; }
};

int main()
{
    // Declare a function pointer by the name of methodA 
    // and initialize it to point to classA::methodB
    void (classA::*methodA)() const noexcept
    {
        &classA::methodB
    };

    // Create an instance of classA
    const auto a = classA{};

    // Call a method on our instance using the function pointer
    (a.*methodA)();
   
    ////////////////////////////////////////////////////////////////
    // A clearer equivalent of the above:

    // Declare an alias for the function pointer type
    using FnPtr = void (classA::*)() const noexcept;

    // Create an instance of the function pointer type,
    // also using brace initialization and pointing to `classA::methodB`
    const auto method = FnPtr{ &classA::methodB };

    // Call the function indirectly through the function pointer
    (a.*method)();    
}

See it live on Coliru, where it prints:

methodB
methodB

One even shorter method if you don't need to know the type (e.g., it's just a local variable, not a member of a class) is that you can use auto to infer the type:

const auto someMethod = &classA::methodB;
(a.*someMethod)();    

If you still don't want to type that alias out but need the type in a context where you can't use auto, you could use decltype to declare it:

// Alias using decltype
using FnPtr = decltype( &classA::methodB );

// Declare instance using decltype (e.g., as a class member)
decltype( &classA::methodB ) method = nullptr;
// ... 
// set it later:
method = &classA::methodB;

// Define template param using decltype
auto fnPtrs = std::vector< decltype( &classA::methodB ) >{};

Upvotes: 2

R Sahu
R Sahu

Reputation: 206707

Any suggestions as to what this is accomplishing?

It is defining a variable named methodA and initializing it to &classA::methodB.

The type of the variable is

a pointer to a const member function of classA that takes no arguments, returns void, and the member function does not throw any exceptions.

The variable is initialized to &classA::methodB.

If you make it a one-liner, it would be:

void (classA::*methodA)() const noexcept {&classA::methodB};

which is equivalent to:

void (classA::*methodA)() const noexcept = &classA::methodB;

Upvotes: 1

Related Questions