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

C++ passing function as argument with polymorphic type

本文关键字:多态 类型 参数 传递函数 C++      更新时间:2023-10-16

>我有函数

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

按钮继承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";
}

假设b属于Button类型

但是如果我这样做b.addOnHover(hoverOver);它将无法编译(一些时髦的编译器错误,一如既往地与这种工作人员)。

如果我做b.addOnHover(std::function<void(game::Button&, game::HoverState)>(hoverOver);或者如果我将悬停从game::ButtonBase&更改为game::Button&,我可以让它工作,但我想知道是否可以在没有如此冗长的情况下完成(std::function...),同时保持参数game::ButtonBase&,因为将来可能会有更多的 Button 类

根据 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>          ]

编辑:

一种可能的解决方案,即使仍然很冗长,也是添加

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

并调用b.addOnHover(game::wrap<game::Button, game::HoverState>(hoverOver);但我不确定它是否会正常运行

您遇到的一个问题是,非常量左值引用不能绑定到右值。

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

或(最好):

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

在您的版本中,参数func是一个非常量左值,当您像这样调用它时,b.addOnHover(hoverOver)hoverOver是指向函数的指针,因此会创建一个类型为std::func(rvalue) 的临时对象,正如我所说,该对象不能绑定到非常量左值引用。

顺便说一句,当我看到错误消息时,我想通了这一点,因此尝试通过错误很重要。

我可以看到两个潜在的问题。

1) "参数"的类型(即按钮)void addOnHover(std::function<void(Button&, HoverState)>& func);比您给出的值更具体ButtonBasevoid hoverOver(game::ButtonBase& button, game::HoverState state)...

这就像您将父类的 ptr 分配给子类一样。

如果您使用了纯值,则ButtonBase&参数应该接受Button&,但不能接受相反。换句话说,我认为你应该有

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

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

2) 即使类型匹配,std::function也不会自动接受来自相同类型的函数指针或 lambda 的转换。使用std::function就像现在在 c++11 中一样很痛苦。您需要先将函数分配给std::function对象,或者像以前一样调用构造函数。不幸的是,这与您可以接受的情况不同std::string接受const char*.

我想您的std::function用法或包装器的作用是类型转换。所以编译器接受它,因为你明确告诉编译器强制转换应该没问题。这并不意味着这些类型实际上是兼容的。

关于第二个问题,我问了一个关于如何让std::function接受相应类型的函数(普通函数、lambda 等)的相关问题:如何使 C++11 函数接受函数<>参数自动接受 lambda。我得到最多的答案是我不应该使用std::function或期望以这种方式使用它。