简体   繁体   English

C ++将函数作为多态类型的参数传递

[英]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 Button继承了ButtonBase ,它是接口类,我想传递

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 假设bButton类型

but if I do b.addOnHover(hoverOver); 但是如果我这样做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 如果我执行b.addOnHover(std::function<void(game::Button&, game::HoverState)>(hoverOver);或者将hoverOver从将game::ButtonBase&更改为game::ButtonBase&可以使其工作game::Button& ,但是我想知道如果没有这种冗长(std :: function ...)并将参数保留为game::ButtonBase& ,是否可以完成game::ButtonBase&因为将来可能会有更多的Button类

as per Bolov request, here is compiler error when passing hoverOver 根据Bolov的要求,这是传递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 并调用b.addOnHover(game::wrap<game::Button, game::HoverState>(hoverOver);但我不确定它是否能正常运行

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. 在您的版本中,参数func是一个非常量左值,当您这样调用它时, b.addOnHover(hoverOver) hoverOver是一个函数指针,因此会创建一个类型为std::func (rvalue)的临时对象,该对象为我已经说过,不能绑定到非常量左值引用。

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); 1)'参数'(即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)... 比您在void hoverOver(game::ButtonBase& button, game::HoverState state)...ButtonBase赋予的值更具体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. 就像您要为父类分配一个ptr,给子类分配一个ptr。

If you had used plain values, a ButtonBase& parameter should accept Button& , but not the other way around. 如果使用普通值,则ButtonBase&参数应接受Button& ,但不能接受。 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)... ) void hoverOver(game::Button& button, game::HoverState state)... (或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. 2)即使类型匹配, std::function也不会自动接受来自相同类型的函数指针或lambda的转换。 It's a pain of using std::function as it is now in c++11. 使用std::function很痛苦,就像在c ++ 11中一样。 You need to assign the function to a std::function object first or call the constructor as you did. 您需要先将函数分配给std::function对象,或者像您那样调用构造函数。 This is, unfortunately, unlike the case where you can have std::string accept const char* . 不幸的是,这不同于可以让std::string接受const char*

I guess what your std::function usage or the wrapper does is a type cast. 我猜你的std::function用法或包装器是类型转换。 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 . 关于第二个问题,我问了一个相关的问题,关于如何让std::function接受相应类型的函数(普通,lambda等): 如何使带有function <>参数的C ++ 11函数自动接受lambda The answer I got most is that I shouldn't use std::function or expect to use it that way. 我最大的答案是我不应该使用std::function或期望以这种方式使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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