假设我们有以下对象:
struct A{
static const int ID = 1;
};
struct B{
static const int ID = 2;
};
struct C{
static const int ID = 3;
};
struct D{
static const int ID = 4;
};
struct Collection{
std::vector<A> o1;
std::vector<B> o2;
std::vector<C> o3;
std::vector<D> o4;
};
Collection collection;
我想做的是获取对 vector
的一些引用Collection
的小号.应该有三种不同的方法来检索这些:
按
vector
的类型,例如collection.get<A>();
通过编译时的 ID,由
vector
持有的每个对象定义,例如collection.get<4>();
- 通过运行时的 ID,例如
collection.get(id);
情况 1 很简单,因为它可以用 T::ID
转换为情况 2| .案例 2 可以通过模板特化来实现(尽管如果我有很多对象,它看起来很难看)。
案例 3 制造了很多麻烦。没有巨人if
或 switch
声明几乎是不可能的,更不用说推断返回类型了。
我的问题是:
- 有没有办法让案例 2 更优雅?
- 我应该如何实现案例 3?
最佳答案
有些事情你可以做,有些事情你永远做不到。你要的不是那么简单,需要努力。
1 和 2 很难,但可以实现。一、create a static map of all type-IDs , 然后 use the static result to choose the type .
现在 3... 嗯...很抱歉告诉你这是不可能的!提供一个反例来证明我的意思:你永远不能创建一个在运行时选择返回类型的函数。但是……
有些事情您可以做,这基本上就是 LLVM 人员为摆脱 dynamic_cast
而做的事情,但它的代码太多了。
首先,case一个基本抽象类,并使所有这些类都派生自它,并使其包含ID:
struct Base{
virtual int getClassID() = 0;
static Base* CreateObject(int ID) //consider using shared_ptr here
{
switch(ID) {
case A::ID:
return new A;
}
case B::ID:
return new B;
default:
return nullptr;
}
//...
}
}
struct A : public Base {
static const int ID = 1;
int getClassID() override {
return ID;
}
};
struct B : public Base {
static const int ID = 2;
int getClassID() override {
return ID;
}
};
struct C : public Base {
static const int ID = 3;
int getClassID() override {
return ID;
}
};
struct D : public Base {
static const int ID = 4;
int getClassID() override {
return ID;
}
};
现在,要实例化一个新类,您可以使用基类。
Base* c = Base::CreateObject(3); //object of C
现在,您终于可以创建自己的魔法转换运算符了:
template <typename T>
T* MyMagicCast(Base* obj)
{
if(obj->getClassID() ISVALID) //Then use the ID to make sure that T matches the ID
{
return static_cast<T*>(obj);
}
else
{
return nullptr;
}
}
这将为您完成运行时的工作。使用这种方法,您的优势在于所有类型的操作都可以通过调用 base 来完成 + 您不使用任何 RTTI。现在只有当您需要特定于您拥有的类型之一(A、B、C、D)的东西时,您才可以使用 MyMagicCast()
来转换为它们的类型。
PS:代码不在我的脑海中,所以可能会有轻微的错别字/错误。
关于C++ 在运行时获取带有 ID 的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45238202/