构建模板类型的编译时列表?

Constructing a compile-time list of templated types?

抱歉问题的措辞含糊。我正在 运行 解决一个看起来应该可以解决的问题,但我的调查有点陷入死胡同。简而言之,我想要一个编译时列表,其中包含从 class A 继承的所有类型。话虽如此,我将直接进入我面临的问题。

假设我有两种类型:实体和组件。

class Entity {
public:
    entity_id id;
    std::vector<component_id> components;
};

template<typename UnderlyingComponentType>
class Component {
public:
     static std::vector<UnderlyingComponentType> components;
     component_id id;
     entity_id containingEntity;
}

简而言之,每个实体包含多个不同类型的组件(例如 TextComponent、SoundComponent、WeightComponent)。然而,不是直接将这些组件作为字段包含,而是仅将它们的 ID 存储在实体中。这允许对相同类型的组件进行快速迭代,因为它们都在一个连续的向量中(例如,可以一次性遍历所有 DescriptionComponents)。同样,可以为任何给定的实体检索给定类型的任何组件:

DescriptionComponent& decription = myEntity.getComponent<DescriptionComponent>()
// Can go to the DescriptionComponent's static vector, and look up the
// appropriate component given our entity's ID (matching it with the component's
// containingEntity ID.)

可以为单个组件的删除或添加提供类似的功能。但是,当我想删除给定实体的 所有 组件时,我 运行 遇到了问题。因为组件存储在实体外部,所以没有简单的说法 "loop through the components you contain and delete them all"。相反,我们必须为我们包含的每个组件类型转到底层静态向量,并从那里删除。这是我碰壁的地方。似乎这里有两种可能的解决方案:

1.) 在每个实体中存储一个类型集合,它映射到我们包含的所有组件类型的组件向量(例如 entity.getComponentTypes == X<DescriptionComponent, SoundComponent>; 这里的缺点是每个实体的组件可以不断变化(我可以在上面的示例中的 运行 时间添加一个 WeightComponent。因此,我不相信有一种方法可以 link 到基于 [=63 的编译时类型=]时间类型信息(如type_indexes等)

2.) 生成实现组件的每种类型的编译时列表,然后在实体要求删除其所有组件时循环遍历它们 all。例如,假设我们有一个实体 e,它具有 ID 123 并包含一个 DescriptionComponent 和一个 SoundComponent。我们仍然会有一个包含 [DescriptionComponent, SoundComponent, WeightComponent] 的类型的编译时列表,并且我们会告诉每个组件的静态向量删除 123 引用的任何组件。由于只有 DescriptionComponentSoundComponent 向量具有这些引用,因此只有这两个向量会删除组件,而 e 的所有组件都会被删除。

上面的选项 2 似乎是更可行的选项:创建类型的编译时列表。手动编码很容易:std::tuple<DescriptionComponent, SoundComponent, WeightComponent> 可以轻松存储并允许我根据需要迭代这些类型。然而,我最理想的是一种以编程方式生成此列表的方法,这样如果我稍后进入并添加一个NameComponent,我就不必更新任何其他代码:当我从我的模板 Component class 继承时,它应该被自动管理。例如,

class NameComponent : public Component<NameComponent> {
    //implementation details
}

//Now, my all-types list should contain [DescriptionComponent, SoundComponent,
// WeightComponent, NameComponent], and I didn't have to update any config.

我完全不确定以上是否在本地可行,但任何帮助或资源建议都是可行的。我在 this Whosebug question 看到过类似的问题,但它依赖于宏(据我了解,不要以相同的方式与模板交互)。同样,如果这看起来像是一个 A/B 问题,而像 type_index 地图这样的简单解决方案实际上 有效,那么这种批评将非常受欢迎还有!我确定我忽略了一些细节或措辞不当,所以请随时告诉我 if/when 如果需要更多信息。

你可以用这个制作类似于 std::type_index 的东西:

template<typename T>
void type_id() {}

using type_id_t = void(*)();

现在在你的地图中,使用这个:

std::map<type_id_t, /* whatever */> myMap;

然后像这样检索:

auto myElement = myMap[type_id</* somthing else */>];

这将在没有 RTTI 的情况下完成工作

您可以使用类型擦除并存储知道要处理哪种组件类型的元素,而不是将 component_id 存储在实体的向量中。

#include <memory>

struct component_id{};

struct component_type_ref
{
  template <typename Comp>
  component_type_ref()
      : _id(Comp::id), _impl(std::make_shared<_impl_t<Comp>>())
  {
  }

  component_id id() const
  {
    return _id;
  }

  void clear()
  {
    _impl->clear();
  }

private:
  struct _impl_base
  {
    virtual void clear();
  };

  template <typename Comp>
  struct _impl_t : public _impl_base
  {
    void clear()
    {
      Comp::components.clear();
    }
  };

  component_id _id;
  std::shared_ptr<_impl_base> _impl;
};

另见 https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil

例如,通过这种方式,您可以在实体的向量中查找组件 ID,然后对其调用 clear。因此,如果您知道要对组件类型 运行 进行的操作列表(并且这些操作不需要是模板)

,那么这可能是一个解决方案

如果不是这样,恐怕您将需要使用类型向量。这里不用元组,一个简单的

template<typename... T> struct my_type_vector{};

可能就足够了。但是正如您自己所写,这在 运行 时间不太可行。