内联文件之间的循环依赖关系

Circular dependancies between inline files

本文关键字:循环 依赖 关系 之间 文件      更新时间:2023-10-16

在我用C++创建的数学库中,我有一个四元数类和一个Vector3类。它们的布局是这样的:

矢量3.hpp:

#pragma once
template<typename T>
struct Vector3
{
   //...
};
template<typename T>
Vector3<T> operator+(Vector3<T> lhs, Vector3<T> rhs);
#include "Vector3.inl"

Vector3.inl

template<typename T>
Vector3<T> operator+(Vector3<T> lhs, Vector3<T> rhs)
{
    return Vector3<T>(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z);
}
//...

Quaternion类的布局相同。

目前,我有使用 Vector3 类作为函数参数的 Quaternion 类,因此它#include Vector3.hpp 标头。但是现在我需要在我的Vector3类中使用Quaternion类来实现某些功能。

由于这仅在函数的实现中需要,因此我通常只将#include放在源文件中,但由于我使用的是内联文件,并且它们包含在标头中,因此这样做只会导致一堆编译器错误,因为它们相互依赖。

当我只能使用头文件/内联文件时,您如何解决这种循环依赖关系?

处理循环依赖关系的最好方法是首先避免它!花一些时间来重新考虑设计、转移功能、拆分类等是明智的。

让我们假设没有循环依赖的情况如下所示:

template <typename T>
struct Vector{
 ...
};
template<typename T>
struct VectorUser{
   Vector<T> v;
   ...
};

如果可以通过前向声明来解决循环依赖问题,则可以解决,例如:

template <typename T>
struct Vector{
  void printout(const VectorUser<T> &user);
  ...
};

VectorUser的前向声明与VectorUser声明后的Vector<T>::printout()实现相结合,即在 VectorUser.inl 中(毕竟,没有人可以在不包含 VectorUser.h 的情况下使用 Vector)产生所需的结果:

//Vector.h:
template <typename T>
struct VectorUser;

template <typename T>
struct Vector{
  void printout(VectorUser<T> &user);
  ...
};
//VectorUser.h:
template<typename T>
struct VectorUser{...} 
};
template<typename T>
void Vector<T>::printout(VectorUser<T> &user){
   user.printout();
}

但是,如果前瞻声明还不够呢?例如:

template <typename T>
struct Vector{
  VectorUser<T> user;
  ...
};

好吧,即使没有模板,您也不会对其进行编译。我经常在代码中看到至少两个选项:

  1. Vector<T>::user作为指针/引用,因此前向声明就足够了。

  2. Vector<T>拆分为两个类:一个基类用于VectorUser,另一个基类派生自它,其所有功能取决于VectorUser

我主张第二种解决方案,但它可能并不总是可行的。