C++模板函数导致循环依赖
Posted
技术标签:
【中文标题】C++模板函数导致循环依赖【英文标题】:C++ template function causes circular dependancy 【发布时间】:2015-04-17 00:24:15 【问题描述】:如何解决模板函数引起的循环依赖?
例如,我定义了一个 Engine 类,它存储一个实体列表,并负责创建实体并从中添加/删除组件。
class Engine
public:
Entity CreateEntity();
template <typename T, typename... Args>
void AddComponentToEntity(Entity& entity, Args... args)
// Code to add component to entity
template <typename T>
void RemoveComponentFromEntity(Entity& entity)
// Code to remove component from entity
private:
std::vector<Entity> entities;
;
然后我还在实体类中添加了函数来“包装”这些函数,从而为向实体添加组件提供了一种很好的语法。
class Entity
public:
template <typename T, typename... Args>
void AddComponent(Args... args)
engine->AddComponentToEntity<T>(*this, args...);
template <typename T>
void RemoveComponent()
engine->RemoveComponentFromEntity<T>(*this);
private:
Engine* engine;
;
这让我可以编写这样的代码
entity.AddComponent<PhysicsComponent>(Arguments..);
不必在任何地方直接引用引擎对象
engine->AddComponentToEntity(entity, Arguments..);
但是,由于引擎类包含实体实例,它必须包含实体类。然后,Entity 类必须包含 Engine 类,以便模板函数调用方法,这会导致循环依赖。
如果函数不是模板,这很容易解决,因为可以将实现放在 Entity.cpp 中,然后我可以在其中包含 Engine 类。我正在努力了解如何使用模板函数完成相同的操作。
【问题讨论】:
在 .inl 中移动实现 可以制作Engine
或Entity
模板参数
从面向对象的角度来看,这对我来说似乎很奇怪。 entity.AddComponent() 向我建议您正在向实体添加一些东西,但在内部涉及引擎对象(因为显然它是唯一能够向实体添加组件的对象)。除了模板循环之外,我认为还有一个设计问题。
这是对实体系统的尝试,同时尝试保留一些 OOP 语法。实体只是一个 ID,具有一些方便的功能来添加、删除和获取组件。引擎实际上处理所有组件的添加、删除等。
【参考方案1】:
您可以通过将定义与声明分开来将函数视为不是模板:
// 引擎.h
#ifndef ENGINE_H
#define ENGINE_H
#include <vector>
class Entity; // forward declaration
class Engine
public:
Entity CreateEntity();
template <typename T, typename... Args>
void AddComponentToEntity(Entity& entity, Args... args);
template <typename T>
void RemoveComponentFromEntity(Entity& entity);
private:
std::vector<Entity> entities;
;
#include "engine.inl"
#endif
// Engine.inl
#ifndef ENGINE_INL
#define ENGINE_INL
#include "engine.h"
#include "entity.h"
template <typename T, typename... Args>
void Engine::AddComponentToEntity(Entity& entity, Args... args)
// Implementation.
template <typename T>
void Engine::RemoveComponentFromEntity(Entity& entity)
// Implementation.
#endif
entity.h/entity.inl 也是如此。
【讨论】:
【参考方案2】:您可以通过先编写类定义,然后编写函数实现来解决它。例如:
class Engine;
class Entity
public:
template <typename T, typename... Args>
void AddComponent(Args... args);
template <typename T>
void RemoveComponent();
private:
Engine* engine;
;
class Engine
public:
Entity CreateEntity();
template <typename T, typename... Args>
void AddComponentToEntity(Entity& entity, Args... args);
template <typename T>
void RemoveComponentFromEntity(Entity& entity);
private:
std::vector<Entity> entities;
;
template <typename T, typename... Args>
void Entity::AddComponent(Args... args)
engine->AddComponentToEntity<T>(*this, args...);
template <typename T>
void Entity::RemoveComponent()
engine->RemoveComponentFromEntity<T>(*this);
void Engine::AddComponentToEntity(Entity& entity, Args... args)
// Code to add component to entity
template <typename T>
void Engine::RemoveComponentFromEntity(Entity& entity)
// Code to remove component from entity
这样您就不必拆分文件。
【讨论】:
【参考方案3】:您的主要问题是课程的设计。包含在entity
中的engine
带有向此类entity
添加组件的责任是毫无意义的。
如果您在添加组件时有不同的行为,具体取决于 Engine,您可以将 Engine 作为 Entity::AddComponent
函数的参数:
template <typename Engine, typename T, typename... Args>
void AddComponent(Engine engine, Args... args)
engine->AddComponentToEntity<T>(*this, args...);
另一方面,您可以使用成语 Curiously recurring template pattern 并将成员 Engine::entities
移动到 Entity::entities
(或 Entity::children
,如果您正在尝试执行某种树数据结构)。
template <typename Entity>
class Engine
public:
Engine()
void addComponent(Entity entity, ...)
class Entity: public Engine<Entity>
// Now this class has the function addComponent
// just like defined in Engine.
private:
std::vector<Entity> children; // Here this has more sense.
;
如果您需要不同的引擎技巧,只需从 Engine
派生并专门/添加需要专门/添加的内容。
【讨论】:
以上是关于C++模板函数导致循环依赖的主要内容,如果未能解决你的问题,请参考以下文章