模板循环依赖 - 在不同的文件中分隔类

时间:2017-12-14 09:32:51

标签: c++ c++11 templates circular-dependency

首先,我想说我之前已经知道这类问题(例如Resolving a Circular Dependency between Template Classes)。

但是,此解决方案(将声明与实现分离)仅在将两个类放入一个文件时才有效。在我的例子中,我有一个StateManager和一个State类,它们都相当大并且保证会增长。因此,将它们放在一个大文件中似乎并不令人满意。

这里有一些重要的代码片段:

// Forward declare the StateManager --> does not work (incomplete type)
class State
{
public:
    template <class TData>
    void RequestStackPush(ID stateId, std::shared_ptr<TData> data);
private:
    StateManager & stataManager;
}

这里是RequestStackPush()方法的实现

template<class TData>
    inline void State::RequestStackPush(ID stateId, std::shared_ptr<TData> data)
    {
        // Uses the state manager's PushState() method - here the issue with the incomplete type arises
        stateManager.PushState<TData>(stateId, data);
    }

显然,StateManager始终使用State类。它创建了调用方法等,所以前向声明在这里没有解决方案。只是给你一个例子:

template<class TData>
    inline void StateManager::PushState(State::ID stateId, std::shared_ptr<TData> data)
    {
        std::unique_ptr<BasePendingChange> pendingChange = std::make_unique<PendingPushDataChange<TData>>(Push, stateId, data);
        pendingChangeQueue.push(std::move(pendingChange));
    }

目前,这两个类都在一个大文件中。首先,声明State类,StateManager为前向声明,然后声明StateManager类,然后执行上述State::RequestStackPush()方法,最后所有StateManager模板方法的实现。

如何将其分成两个不同的文件?

1 个答案:

答案 0 :(得分:1)

  

但是,这个解决方案(将声明与实现分开)仅在将两个类放入一个文件时才有效。

不,它不仅适用于一个文件。您始终可以通过包含子标题来构造相同的文件。它只需要你做一些非模板不常见的事情(虽然相同的技术适用于所有内联函数定义):你需要在定义类之后包含一个文件。尽管给出了名称,但标题不仅限于文件的顶部。

所以,在一个文件中:

  • 声明StateManager
  • 定义State
  • 包含StateManager
  • 的定义
  • 定义依赖于StateManager
  • 定义的成员函数

其他文件中没有异常:

  • 包含State
  • 的定义
  • 定义StateManager及其成员函数。

最终结果是包含任一标题以所需顺序生成相同的定义和声明。因此,这种文件拆分绝不会有助于限制修改其中一个标头所引起的重新编译量。

这可能是一个品味问题,但我总是在定义类之后包含内联函数(包括模板和模板函数的成员)所需的定义。这样我就不用担心这样做是否必要。