首先,如果问题的名称不够清楚,请对不起。我真的不知道如何称呼这个问题。
因此,我在类内有一个函数指针,该类的工作方式类似于我使用从父类派生的某些参数(如本例中这样的参数)调用的java回调:
class Parent;
using f_Call = void(*)(Parent*);
class Parent
{
public:
void setCallback(f_Call call)
{
mOnCall = call;
}
protected:
f_Call mOnCall = nullptr;
};
class Child1 : Parent
{
public:
void doSomething()
{
// some work..
if (mOnCall)
mOnCall(this);
}
};
void onCallExe(Parent* p)
{
Child1* child = (Child1*)p;
// do some more work...
}
int main()
{
Child1 child;
child.setCallback(onCallExe);
child.doSomething();
}
我的问题是c ++是否可以在onCallExe中自动执行从父对象到子对象的转换,因此我不必为我调用的每个函数都进行转换。
谢谢!
不要使用函数指针。相反,您需要std::function<void()>
(是的,without参数),并将其传递给带有captured对象的lambda。
using f_Call = std::function<void()>;
class Parent {
public:
void setCallback(f_Call call) {
mOnCall = call;
}
protected:
f_Call mOnCall;
};
class Child1 : public Parent {
public:
void doSomething() {
// some work..
if (mOnCall)
mOnCall(); // no argument!
}
};
int main() {
Child1 child;
child.setCallback([&child](){ /* do whatever with the child */ });
child.doSomething();
}
如果需要,您可以在函数模板中隐藏lambda的创建。
template <class Obj, class CB>
void setCallback (Obj& obj, CB cb) {
obj.setCallback([&obj](){cb(obj);});
}
然后将带有Child参数的任何旧函数传递给全局setCallback模板。
void onCallExe(Child1& child) {
// do some more work...
}
Child1 child;
setCallback(child, onCallExe);
Curiously recurring template pattern可能是一个选项。但是,您将为每个派生的类创建单独的基类,因此您不能多态使用它们-除非您提供单独的通用基类。如果循环模板功能覆盖了基础中的虚拟模板,那么您可能会到达想要的位置:
struct Base
{
virtual ~Base() { }
virtual void f() = 0;
};
template <typename T>
struct Intermediate : Base
{
void f() override
{
if(callback)
callback(static_cast<T*>(this));
}
void setCallback(void(*c)(T*))
{
callback = c;
}
private:
void(*callback)(T*) = nullptr;
};
struct Derived : Intermediate<Derived>
{
};
void g(Derived*) { }
void demo()
{
Derived d;
d.setCallback(g);
d.f();
}
((如果不需要多态性,则可以跳过Base
类-这样f
也不必是虚拟的。)
唯一地,如果您想通过指针或对Base
的引用来设置回调,则会遇到麻烦,因为您无法使用虚拟模板成员函数。尽管可以提供独立的帮助器功能,但您可以:
template <typename T>
void setCallback(Base& b, void(*callback)(T*))
{
dynamic_cast<Intermediate<T>&>(b).setCallback(callback);
}
如果std::bad_cast
的类型不合适,则动态转换将抛出b
–不幸的是,这是相当昂贵的运行时事物,让编译器确定指向/引用的对象是否为正确类型的安全方法(通常)是不可能。