10

几天来我一直在努力解决这个问题,我阅读了很多关于新 C++20 模块的文档和帖子,其中这个官方模块,这个另一个在 Stackoverflow 上,但我真的无法解决这个问题.

我正在使用随Visual Studio Preview 16.6.0 2.0提供的 MSVC 编译器。我知道它还不是一个稳定的版本,但我想弄乱新功能来开始学习它们。

基本上我写了一个模块(myModule)和这个模块的两个分区(mySubmodule1mySubmodule2),我在两个模块实现文件(mySubmodule1Impl.cppmySubmodule2Impl.cpp)中实现了它们。

mySubmodule1依赖于mySubmodule2,反之亦然。这是来源:

mySubmodule1.ixx

export module myModule:mySubmodule1;

export namespace myNamespace{

class MyClass2;

class MyClass1{
    public:
    int foo(MyClass2& c);
    int x = 9;
};
}

mySubmodule2.ixx

export module myModule:mySubmodule2;
import :mySubmodule1;

export namespace myNamespace{

class MyClass2 {
    public:
    MyClass2(MyClass1 x);
    int x = 14;
    MyClass1 c;
};
}

mySubmodule1Impl.cpp

module myModule:mySubmodule1;
import :mySubmodule2;

int myNamespace::MyClass1::foo(myNamespace::MyClass2& c) {
    this->x = c.x-14;
    return x;
}

mySubmodule2Impl.cpp

module myModule:mySubmodule2;
import :mySubmodule1;

myNamespace::MyClass2::MyClass2(myNamespace::MyClass1 c) {
    this->x = c.x + 419;
}

我的模块.ixx

export module myModule;

export import :mySubmodule1;
export import :mySubmodule2;

正如你所看到的,我可以转发声明MyClass2mySubmodule1但我不能转发声明MyClass1mySubmodule2因为MyClass2我使用类型的具体对象MyClass1

我用这一行编译:cl /EHsc /experimental:module /std:c++latest mySubmodule1.ixx mySubmodule2.ixx myModule.ixx mySubmodule1Impl.cpp mySubmodule2Impl.cpp Source.cppwhere Source.cppis just main.

我收到臭名昭著的错误 C2027: use of undefined type 'myNamespace::MyClass2' in mySubmodule1Impl.cppand mySubmodule2Impl.cppat the lines where I use MyClass2. 此外,编译器告诉我在有前向声明的地方查看 in 的声明MyClass2mySubmodule1.ixx

现在,我真的不明白我的错误在哪里。我一遍又一遍地检查,但程序的逻辑对我来说似乎很完美。文件的编译顺序应该MyClass2在实现之前定义!

我尝试使用“旧”.h 和 .cpp 文件而不是模块来编译这个确切的程序,它编译并运行良好。所以我想我错过了关于这些新模块的一些东西。

我检查了第一个正式的模块提案(第 10.7.5 段),在第一个提案中有一个名为宣告所有权声明的构造,在这种情况下似乎是完美的。基本上,它允许您导入当前模块中另一个模块拥有的实体,但不导入模块本身。但在该提案的后期修订中,没有任何迹象。绝对没有。在新提案的“变更日志”部分甚至没有引用它。

请不要告诉我循环依赖是不好的。我经常知道它们很糟糕,但并非总是如此。即使您认为它们总是很糟糕,我也不会要求经验法则。我在问为什么我的代码用“旧” .h + .cpp 编译而不是新模块。为什么链接器看不到MyClass2.


编辑 1

这是答案中建议的新设计,但它仍然不起作用。我得到完全相同的错误:

mySubmodule1Impl.cpp

module myModule;

int myNamespace::MyClass1::foo(myNamespace::MyClass2& c) {
    this->x = c.x-14;
    return x;
}

mySubmodule2Impl.cpp

module myModule;

myNamespace::MyClass2::MyClass2(myNamespace::MyClass1 c) {
    this->x = c.x + 419;
}

所有其他文件均未更改。

4

3 回答 3

7

最直接的问题是你不能为单个模块分区拥有一个“接口文件”一个“实现文件” (就好像它是一个头文件和源文件对一样)。有接口分区和实现分区,但每个分区都必须有自己的名称,因为每个分区都是要导入的。当然,模块的目的之一是允许需要头/源对的单个文件:您通常可以将实现包含在与接口文件相同的文件中,但使用和/或仅与后者一起使用。这确实带来了导致更频繁的下游重建的通常仅标题的缺点。exportinline

元问题是这里没有循环:你已经用MyClass2. 这是正确的做法:模块不会改变 C++ 的基本语义,因此这些技术仍然适用且必要。出于通常的组织原因,您仍然可以将类分成两个文件,但是方法定义根本不需要在分区中(也不需要在单独的module myModule;实现单元中,它会自动导入所有接口)。剩下import :mySubmodule1的(在接口分区中mySubmodule2)是明确和正确的。

至于proclaimed-ownership-declaration,它们出现在没有模块分区的 Modules TS 中,因此无法以其他方式处理此类情况(因为您可以对来自另一个分区的实体使用正常的前向声明,但不能另一个模块)。

于 2020-03-28T16:43:03.323 回答
0

当然。如果我能够直接回复帖子,那么答案在上下文中更有意义。上面的链接是对 Touloudou 最后一个答案的回复,并附有额外的参考资料。该解决方案:从分区导出前向声明(可选地将导出分隔到它们自己的分区中)。

此外,在撰写该网站帖子时,禁止跨模块交叉引用并且 gcc 支持滞后,这可以解释当时其他早期答案中遇到的问题和可能的误导。

于 2022-01-25T18:14:17.270 回答
0

请参阅我对这篇文章的回答。您可能需要导出前向声明以获得外部链接而不是模块链接MyClass2

于 2021-10-07T09:25:14.160 回答