Elliott
Elliott

Reputation: 2634

function objects for class methods

I know how to make a function an input argument when the function isn't a method, but I can't figure out how to do it when the function is a method.

Here's what I tried:

#include <iostream>

class A
{
    void funct_1(int a, void (A::*f)(int))
    {
        (A::*f)(a);
    }

public:

    void funct_2(int k)
    {
        // do something
    }

    void funct_3(int k)
    {
        // do something
    }

    void funct_4(int k)
    {
        // some reason to use the function argument functionality...
        if (k % 2)
            funct_1(k, funct_2);
        else
            funct_1(k, funct_3);
    }
};

int main()
{

    A a;

    a.funct_4(4);

    return 0;
}

The above code doesn't compile. I've tried lots of variations, but can't find any syntax use that works. Could someone explain what I'm doing wrong here in terms of the syntax?

Upvotes: 4

Views: 572

Answers (2)

Moia
Moia

Reputation: 2364

I don't know if you need to use an exact signature, but can't you just do this?

template<typename Func>
void funct_1(int a, Func&& func)
{
   std::forward<Func>(func)(a);
}

the signature of your method does not change:

void funct_4(int k)
{
    // some reason to use the function argument functionality...
    if (k % 2)
        funct_1(k, funct_2);
    else
        funct_1(k, funct_3);
}

you can even extends it for any arbitrary number of parameters

template<typename Func>
void funct_1(Func&& func, Args&&... args)
{
   std::forward<Func>(func)(std::forward<Args>(args)...);
}

Those allow to use function as parameter. If you intend to use it inside your class in the way you've showed it's mostly a design-flaw and re-think the class structure.

If you want to pass around member function inside your class enclose it in a lambda and std::function, so you can use it as variables and use the method above.

If you just want a clean way for your example, actually do exists an extremely way:

void funct_1(int a, auto&& func)
{
   (this->*func)(a);
}

So your sample become

#include <iostream>
#include <functional>

class A
{
    void funct_1(int a, auto&& func)
    {
       (this->*func)(a);
    }
    
public:
    void funct_2(int k)
    {
        std::cout << "func 2" << std::endl;
    }

    void funct_3(int k)
    {
        std::cout << "func 3" << std::endl;
    }

    void funct_4(int k)
    {
        // some reason to use the function argument functionality...
        if (k % 2)
            funct_1(k, &A::funct_2);
        else
            funct_1(k, &A::funct_3);
    }
};

int main()
{
    A a;
    a.funct_4(4);

    return 0;
}

Live demo

Upvotes: 3

lubgr
lubgr

Reputation: 38325

The pointer to member syntax is always hard to figure out. You are always there, just change the first member function to

void funct_1(int a, void (A::*f)(int))
{
    (this->*f)(a);
}

and the fourth to

void funct_4(int k)
{
    if (k % 2)
        funct_1(k, &A::funct_2);
    else
        funct_1(k, &A::funct_3);
}

For you original attempt to call the member function, note that (A::*f)(a); is conceptually wrong, as it doesn't associate the member function pointer f with an instance of A. Member function pointers are offsets into a class definition, but not tight to a particular instance of that class. This must be provided when doing the actual call. Specific operator exist to do exactly that, i.e. .* and ->* as in this->*f. As their precedence is low, you need to add an additional set of parentheses: (this->*f).

Upvotes: 3

Related Questions