如何避免 header only 库中的循环依赖?

How can I avoid circular dependencies in a header only library?

我正在开发一个仅 C++ header 的库。

代码中有几个部分遵循这种模式:

holder.h

#pragma once

#include "sub.h"

struct Holder
{
    void f();
    Sub s;
};

sub.h

#pragma once

struct Holder;

struct Sub
{
   void g(Holder& h);
};

#include "sub.ipp"

sub.ipp

#include "holder.h"

inline void Sub::g(Holder& h)
{
    h.f();
}

sub.h 使用前向声明避免了对 Holder 的循环依赖。然而,在 holder.h 作为 Holder class 包含一个 Sub 成员它需要在 sub.h 中查看 Sub 的完整声明。然而,sub.h 引入了 sub.ipp 中的实现,它还不能实例化,因为它需要定义Holder 并且我们已经在 holder.h 中,所以我们不能再包含它了。

作为这些 header 中任何一个的用户,我只想包含正确的 .h 文件,而不必担心手动包含在奇怪的地方纠正 .ipp 个文件。

这个问题的标准解决方案是什么?

struct Sub
{
   void g(Holder& h);
};

void Sub::g(Holder& h)
{
    h.f();
}

Non-inline 函数在 header-only 库中无法正常工作,因为 header 通常包含在多个翻译单元中。您应该改用内联函数。


How can I avoid circular dependencies in a header only library?

您必须将函数的定义与 class 的定义分开。我的意思是,它们已经在单独的文件中,但是定义 class 的 header 不能包含函数定义。这允许打破依赖循环。

这可能是个人喜好问题,但我也不喜欢不能独立运行的“ipp”header。

示例:

detail/holder_class_only.h

#pragma once
#include "detail/sub_class_only.h"
struct Holder
{
    inline void f(); // note inline
    Sub s;
};

detail/sub_class_only.h

#pragma once
struct Holder;
struct Sub
{
   inline void g(Holder& h); // note inline
};

detail/holder_functions.h

#pragma once
#include "detail/holder_class_only.h"
void Holder::f()
{
}
#include "detail/sub_functions.h"

detail/sub_functions.h

#pragma once
#include "detail/sub_class_only.h"
#include "holder.h"
void Sub::g(Holder& h)
{
    h.f();
}

sub.h

#pragma once
#include "detail/sub_class_only.h"
#include "detail/sub_functions.h"

holder.h

#pragma once
#include "detail/holder_class_only.h"
#include "detail/holder_functions.h"

注意:未经测试,可能包含错误。