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 中移动实现 可以制作EngineEntity模板参数 从面向对象的角度来看,这对我来说似乎很奇怪。 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++模板函数导致循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

解决由于类之间的循环依赖而导致的构建错误

解决由于类之间的循环依赖而导致的构建错误

解决由于类之间的循环依赖而导致的构建错误

解决由于类之间的循环依赖而导致的构建错误

解决由于类之间的循环依赖而导致的构建错误

Ngrx Store 作为提供者中的依赖关系导致循环依赖