简体   繁体   中英

C++ passing function as argument with polymorphic type

I have functions

void addOnHover(std::function<void(Button&, HoverState)>& func);
void addOnHover(void(*fptr)(Button&, HoverState));

Button inherits ButtonBase , which is interface class and I want to pass

void hoverOver(game::ButtonBase& button, game::HoverState state)
{
    static int i = 0;
    if (state == game::HoverState::STATE_ENTER)
        std::cout << "entered " <<    button.getLabel().getText().getString().toAnsiString() << " " << ++i << "\n";
    else if (state == game::HoverState::STATE_LEAVE)
        std::cout << "left " << button.getLabel().getText().getString().toAnsiString() << " " << ++i << "\n";
    else
        std::cout << "inside " << button.getLabel().getText().getString().toAnsiString() << " " << ++i << "\n";
}

lets say b is of type Button

but if I do b.addOnHover(hoverOver); it will not compile(some funky compiler error, as always with this kind of staff).

I can make it work if I do b.addOnHover(std::function<void(game::Button&, game::HoverState)>(hoverOver); or if I change the hoverOver from taking game::ButtonBase& to game::Button& , but I wonder if it could be done without such verbosity(std::function...) and while keeping the argument as game::ButtonBase& because there may be more Button classes in future

as per Bolov request, here is compiler error when passing hoverOver

1>Source.cpp(58): error C2664: 'void game::Button::addOnHover(std::function<_Fty> &)' : cannot convert parameter 1 from 'void (__cdecl *)(game::ButtonBase &,game::HoverState)' to 'std::function<_Fty> &'
1>          with
1>          [
1>              _Fty=void (game::Button &,game::HoverState)
1>          ]

edit:

One possible solution, even tho still pretty verbose is to add

template <class T, class C, class Q>
std::function<void(T&, C)> wrap(Q ptr)
{
    return std::function<void(T&, C)>(ptr);
}

and call b.addOnHover(game::wrap<game::Button, game::HoverState>(hoverOver); but Im not sure if it will function correctly yet

One problem that you have is that an non-const lvalue reference cannot be bound to an rvalue.

make

void addOnHover(const std::function<void(Button&, HoverState)>& func);

or (preferably):

void addOnHover(std::function<void(Button&, HoverState)> func);

With your version the parameter func is a non-const lvalue, and when you call it like this b.addOnHover(hoverOver) hoverOver is a pointer to function so a temporary object is created of type std::func (rvalue) which, as I have said, cannot be bound to a non-const lvalue reference.

Btw, i figured this out when I saw the error message, so it is important to try to go through the error.

There are two potential issues I can see.

1) The type of the 'parameter' (ie Button) void addOnHover(std::function<void(Button&, HoverState)>& func); is more specific than the value you give ButtonBase in void hoverOver(game::ButtonBase& button, game::HoverState state)...

This is as if you are assigning a ptr to the parent class to a ptr to a child class.

If you had used plain values, a ButtonBase& parameter should accept Button& , but not the other way around. In other words, I think you should have

void addOnHover(std::function<void(ButtonBase&, HoverState)>& func);

and

void hoverOver(game::Button& button, game::HoverState state)... (or void hoverOver(game::ButtonBase& button, game::HoverState state)... )

2) even if the types do match, std::function does not automatically accept conversion from a function pointer or lambda of the same type. It's a pain of using std::function as it is now in c++11. You need to assign the function to a std::function object first or call the constructor as you did. This is, unfortunately, unlike the case where you can have std::string accept const char* .

I guess what your std::function usage or the wrapper does is a type cast. So compiler accepts it because you explicitly told the compiler the cast should be OK. It doesn't mean the types are actually compatible.

Regarding the second issue, I have asked a related question about how to let std::function accept a function(plain, lambda etc) of the corresponding type: How to make C++11 functions taking function<> parameters accept lambdas automatically . The answer I got most is that I shouldn't use std::function or expect to use it that way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM