2017-07-21 10 views
1

让我们假设我们有以下对象:C++掌握对象的ID在运行时

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 S中Collection的。应该有三种不同的方式来获取这些:

  1. 通过类型vector,例如collection.get<A>();

  2. 由编译时的ID决定,它是由vector所保存的每个对象定义的。 collection.get<4>();

  3. 通过运行时的ID,例如collection.get(id);

案例1很容易,因为它可以通过T::ID转换为案例2。情况2可以通过模板专门化来实现(尽管如果我有很多对象,它看起来很难看)。 案例3造成了很多麻烦。没有一些巨大的ifswitch声明它几乎不可能,更不用说推断返回类型。

我的问题是:

  • 有没有一种方法,使案件2更优雅?
  • 我该如何实施案例3?
+0

只会有'A,B,C,D'吗? –

+0

可能不会,至少有30个。 – Overblade

+0

您有一个运行时间值。你不知道它是什么。您需要根据该值操作不同的类型,这意味着完全不同的代码路径(即使它是同一个函数模板的不同实例化)。这意味着一个运行时分支。所以,你可以有一个'switch'或'map ' - 这些都是你的选择。 – Useless

回答

1

有些事情你可以做,而你永远无法做的事。你所要求的并不那么简单,需要努力工作。

1和2很难但可以实现。首先,create a static map of all type-IDs,然后use the static result to choose the type

现在3 ...好...很遗憾地告诉你这是不可能的!提供一个反例来证明我的意思:你永远不能创建一个在运行时选择返回类型的函数。但...

你可以做些什么,这基本上是LLVM人为了摆脱dynamic_cast而做的,但是代码太多了。

首先,案例抽象基类,使所有这些类从它派生,并使其包含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:代码不在我的头顶,所以可能会有轻微的错别字/错误。

+0

感谢你的例子,我会看看它。 – Overblade