为了了解有关 C++20 模块的更多信息,我正在将图形应用程序从头文件迁移到模块。目前我遇到两个类之间的循环依赖问题。这两个类描述了图的节点和边。边类具有指向两个节点的指针,并且节点类具有指向相邻边的指针向量。我知道,还有其他方法来描述图形,但这种架构对我来说似乎非常自然,我可以非常快速地访问相邻元素,并且它在头文件和#include 的旧世界中无缝工作。关键是前向引用。
但在 C++20 模块的新世界中,前向引用不再起作用。
循环引用的话题已经在很多地方讨论过,但我还没有找到真正让我信服的解决方案。
一个常见的说法是循环引用是一个架构问题,应该避免。如有必要,应将这两个类打包到一个模块中。这显然是一种倒退。我尝试让模块变得小而简单。
我可以用指向实际已经存在的公共基类 NetworkObject 的指针替换指向节点或边的指针。但这会破坏有价值的信息,并迫使我使用 static_cast 人为地将类型信息添加回来。
我的问题是:我错过了什么吗?有更容易的方法吗?
我在这里看到一些误解。不完全错误,但也不完全正确。
但在 C++20 模块的新世界中,前向引用不再起作用。
这并不完全正确。您不能使用前向引用将某些内容声明为不同模块的一部分,但您当然可以在同一模块中执行此操作。
例如:
export module M;
export namespace n {
struct B;
struct A {
B* b;
};
struct B {
A* a;
};
}
Run Code Online (Sandbox Code Playgroud)
然后你可以将它分成多个模块分区:
export module M;
export namespace n {
struct B;
struct A {
B* b;
};
struct B {
A* a;
};
}
Run Code Online (Sandbox Code Playgroud)
export module M:a;
namespace n {
struct B;
export struct A {
B* b;
};
};
Run Code Online (Sandbox Code Playgroud)
export module M:b;
namespace n {
struct A;
export struct B {
A* b;
};
};
Run Code Online (Sandbox Code Playgroud)
其要点是,要定义的相互依赖的类型具有足够的耦合性,因此它们必须驻留在同一模块中。
另外,请注意,模块不一定像标头一样精细。过多地划分模块可能会损害编译时性能。例如,整个库可能只是一个大模块。标准库选择了这种方法并导出std模块中的所有内容,结果证明它比将标准库划分为许多较小的模块更快。
较小的模块并不像许多人想象的那么好。相关的事物和类应该打包在同一个模块中,如果该模块内的代码组织需要进一步拆分,则可以选择分区。
模块的数量及其名称是 API 的一部分。这意味着,如果您有太多细粒度的模块,只需移动代码就会导致重大更改。模块分区不是 API 的一部分,可以自由移动。
一个常见的说法是循环引用是一个架构问题,应该避免。如有必要,应将这两个类打包到一个模块中。这显然是一种倒退。我尝试让模块变得小而简单。
由于它们之间的循环,这些模块不会很小而且很基本。即你不能只使用一个模块而不使用另一个模块。如果实现驻留在另一个静态库中,您将需要链接到另一个模块。
这两个类描述了图的节点和边
我们有一个程序只能使用节点模块或边缘模块吗?几乎不。它们应该是graph模块的一部分。您可以有:edge和:node分区,但在程序或程序的一部分中仅使用其中一个是没有意义的。
如果这是为了编译时间,那么今天已经证明,使用当前编译器技术制作更大的模块比较小的模块更快
将模块拆分为更小的模块的基本原理是,存在只想导入某些特定内容的用例。例如,std.freestanding仅包含标准库的独立部分,因此程序员不会意外使用他们不允许使用的部分。
当然,另一种方法是放弃所有模块保护措施并使用全局模块片段(GMF)。使用它允许模块与隐式全局模块进行交互。是的,使用它可以带来全球前瞻性声明带来的好处和后果。您将为 ODR 违规再次成为可能开辟道路,并且您的实体将不再是命名模块的一部分。它还允许用户使用您的实体,而无需导入声明所在的特定命名模块,绕过您通过模块名称向用户公开的 API。
您可以使用以下指令打开潘多拉魔盒extern "C++":
export module A;
export namespace n {
extern "C++" {
struct B;
struct A {
B* b;
};
}
}
Run Code Online (Sandbox Code Playgroud)
export module B;
export namespace n {
extern "C++" {
struct A;
struct B {
A* a;
};
}
}
Run Code Online (Sandbox Code Playgroud)