【发布时间】:2019-01-23 04:42:58
【问题描述】:
我想创建一个库:-
- 用户通过
addCallback<Base>(Callback* callback)添加回调(通常在第一个时间步) -
以后,通常在不同的
.cpp,每当用户调用actor<Bx>():-- 如果
Bx继承自Base,调用callback->callback() - 否则什么都不做
- 如果
(信息)我确定每个
Bx总是从A继承。
这里是初始代码:-
#include <iostream>
class A{};
class Callback{
public: virtual void callback()=0;
};
template<class Base> void addCallback(Callback* callback){
//???
};
template<class Bx> void actor(){
//???
}
//^^^^^^ my library end here
class B : public A{};
class B1 : public B{};
class C : public A{};
class CallbackCustom1: public Callback{
public: virtual void callback(){std::cout<<"A"<<std::endl;}
};
class CallbackCustom2: public Callback{
public: virtual void callback(){std::cout<<"B"<<std::endl;}
};
int main(){
CallbackCustom1 call1;
CallbackCustom2 call2;
addCallback<A>(&call1);
addCallback<B>(&call2);
//vvv below is usually in another .cpp
actor<B1>(); // should print "A" and "B"
actor<C>(); // should print "A" only
}
怎么做?
我的糟糕解决方案
解决方案 1:std::is_base_of
我真的很喜欢使用std::is_base_of<Base,Derive>。
但是,这是不可能的,因为为了方便,用户只想在actor<Bx>() 中调用单个类型Bx。std::is_base_of 需要两个类的名称而不是一个。
解决方案 2 (MCVE demo):虚拟析构函数 + std::function
它可以进一步优化,但我想保持简单:-
#include <iostream>
#include <functional>
class A{public: virtual ~A()=default; };
class Callback{
public: virtual void callback()=0;
};
class MyTuple{public:
std::function<bool(A*)> func;
Callback* callback;
};
std::vector<MyTuple> myTuples;
template<class Base> void addCallback(Callback* callback){
std::function<bool(A*)> func=
[](A* a){return dynamic_cast<Base*>(a)!=nullptr;};
MyTuple tuple; tuple.func=func; tuple.callback=callback;
myTuples.push_back(tuple);
}
template<class Bx> void actor(){
Bx b;
for(auto tuple:myTuples){
if(tuple.func(&b)){
tuple.callback->callback();
}
}
}
//^^^^^^ my library end here
它有效,但有一些缺点:-
- 我必须将 virtual destructor 添加到
A以使其 多态。我觉得这是一个讨厌的黑客。 - 在我的游戏中,在某些时间步长中,
A::~A()每秒可能被调用 >100,000 次。
我可以通过 makeB1和Cfinal 来降低成本,并通过派生类进行批量删除,但在某些地方,它不合适且不方便。 - 我必须为 dynamic_cast 检查创建
Bx的实例。
如果它的构造函数做一些特殊的事情,这可能会导致一些复杂性。
有没有更好的方法?
【问题讨论】:
-
想到一个更糟糕的 hack:
throw和catch可能被滥用来在运行时测试非多态类之间的公共继承关系。如果你敢于尝试,你可以使用std::type_index来缓存结果,并确保每个遇到的新类型只执行一次该步骤。 -
@aschepler 喜欢 Cassio Neri's solution in stackoverflow.com/questions/18099241/… ?
-
类似的想法,是的,但是对于您的用例,
throw和catch需要在不同的函数中。
标签: c++ templates inheritance c++14 dynamic-cast