Midnight Exigent
Midnight Exigent

Reputation: 625

How to pass an overloaded member-function as parameter?

Here is the problem I am facing: I have an overloaded function in a class, and I want to pass one of its overloads as a parameter. But when doing so, I get the following error :

"no suitable constructor exists to convert from <unknown-type> to std::function<...>"

Here's a code sample to illustrate that :

#include <functional>
#include <string>

class  Foo
{
private:
    int val1 , val2;
};    

class Bar
{
public:
    void function ( ) {
        //do stuff;
        Foo f;
        function ( f );
    }    
    void function ( const Foo& f ) {
        //do stuff
    }

private:
    //random attribute
    std::string str;    
};


void otherFunction ( std::function<void ( Bar& , const  Foo& ) > function ) {
    Bar b;
    Foo f;
    function(b,f);
}

int main ( ) {    
    otherFunction ( &Bar::function );
                    ^^^
                   error
}

I understand that the compiler cannot deduce which overload to use, so the next best thing to do is a static_cast, but the following code still has the same error

std::function<void ( Bar& , const Foo& )> f = static_cast< std::function<void ( Bar& , const Foo& )> > ( &Bar::function );

Upvotes: 5

Views: 1274

Answers (3)

Andriy Tylychko
Andriy Tylychko

Reputation: 16286

You need to cast to member-function pointer, not to std::function:

otherFunction ( static_cast<void(Bar::*)(const Foo&)>(&Bar::function) );

Live

[EDIT]

Explanation:

otherFunction ( &Bar::function );

otherFunction takes std::function as a parameter. std::function has an implicit constructor (an implicit conversion) from a function pointer (a member function or a free function, and other "callable" types, doesn't matter here). It looks like this:

template< class F > 
function( F f );
  • it's a template parameter
  • while F is "callable", it doesn't specify the signature of F

This means that compiler doesn't know which Bar::function you meant, because this constructor doesn't put any restrictions on input parameter. That's what compiler is complaining about.

You tried

static_cast< std::function<void ( Bar& , const Foo& )> > ( &Bar::function );

While it looks like compiler has all details it needs here (the signature), actually the same constructor is called, so nothing effectively changed. (Actually, the signature is incorrect, but even correct one wouldn't work)

By casting to a function pointer we provide its signature

static_cast<void(Bar::*)(const Foo&)>(&Bar::function)

So ambiguity is resolved as there's only one such function so compiler is happy.

Upvotes: 8

David G
David G

Reputation: 96855

You may need to use a macro in order to avoid a few headaches.

#define OVERLOADED(f) \
  [&](auto&&...xs)noexcept->decltype(auto)\
    {return std::invoke(f, decltype(xs)(xs)...);}

int main(){
  g(OVERLOADED(&Bar::function));
}

Upvotes: 2

JeJo
JeJo

Reputation: 33022

If you use the typed member function pointer along with a templated otherFunction, your code will work. That means, change your otherFunction() to:

template<typename Class, typename T>
void otherFunction(void(Class::*)(T) ) {
    Bar b;
    Foo f;
    b.function(f);
}

If the syntax is confusing, use a helper (template)alias for member function pointer:

template<typename Class, typename T>
using MemFunPtr = void(Class::*)(T);

template<typename Class, typename T>
void otherFunction(MemFunPtr<Class, T> function) {
    Bar b;
    Foo f;
    b.function(f);
}

Now you can call the function without typecasting.

int main()
{
    otherFunction(&Bar::function);
    return 0;
}

(See Online)

Upvotes: 3

Related Questions