C++模块从另一个模块转发声明实体
我最近一直在尝试使用GCC 11将一个代码库转换为C++20模块。然而,我在以下情况下卡住了。首先,下面是使用Header的方法:
A.H
class B;
class A {
public:
void f(B& b);
};
A.cpp
#include "A.h"
#include "B.h"
void A::f(B& b)
{
// do stuff with b
}
(B.H的内容在这里并不重要)
需要注意的是B的向前声明,并不是每个使用A的人都应该关心B,所以我使用了向前声明来阻止重新编译的发生。对于页眉,这种情况可以完美地工作。问题出在尝试将此代码转换为模块时。主要问题是实体与声明它们的模块捆绑在一起,因此不可能在A.H中向前声明。我尝试在全局模块中进行正向声明,但编译器仍然抱怨B的定义与其声明位于不同的模块中。我还尝试了第三个模块,它只包含B的转发声明,但这仍然是在两个不同的模块中声明和定义B。因此,我的主要问题是:如何从模块外部转发声明内容?我还会对最终产生相同效果的方法感到满意:当B更改时,不需要重新编译A的用户。
在搜索时,我发现有几个地方谈论类似的情况,但由于某种原因,它们都不起作用。它们不起作用的原因:
- 有人说有一个带有转发声明的模块。正如我上面所说的,这是行不通的。
- 有人说要用声明的所有权声明。但是,它们已从最终的C++标准中删除。
- 有人说要用模块分区。然而,只有当A和B在同一模块中时,这才起作用。A和B不应在同一模块中,因此这不起作用。
编辑:作为对评论的回应,以下是我尝试过的几种方法的详细信息:
尝试1:在A.mpp中转发声明B
A.mpp
export module A;
class B;
export class A {
public:
void f(B& b);
};
B.mpp
export module B;
export class B {};
A.cpp
module A;
import B;
void A::f(B& b)
{
// do stuff with b
}
执行此操作时,GCC与
A.cpp:4:11: error: reference to ‘B’ is ambiguous
4 | void A::f(B& b)
| ^
In module B, imported at A.cpp:2:
B.mpp:3:14: note: candidates are: ‘class B@B’
3 | export class B {};
| ^
In module A, imported at A.cpp:1:
A.mpp:3:7: note: ‘class B@A’
3 | class B;
尝试2:在新模块中转发声明
B_decl.mpp
export module B_decl;
export class B;
A.mpp
export module A;
import B_decl;
export class A {
public:
void f(B& b);
};
B.mpp
export module B;
import B_decl;
class B {};
A.mpp
module A;
import B;
void A::f(B& b)
{
// do stuff with b
}
执行此操作时,GCC与
B.mpp:5:14: error: cannot declare ‘class B@B_decl’ in a different module
5 | class B {};
| ^
In module B_decl, imported at B.mpp:3:
B_decl.mpp:3:14: note: declared here
3 | export class B;
尝试3:在头中转发声明,在模块中定义
B_decl.h
class B;
A.mpp
module;
#include "B_decl.h"
export module A;
export class A {
public:
void f(B& b);
};
B.mpp
module;
#include "B_decl.h"
export module B;
class B {};
A.cpp
module A;
import B;
void A::f(B& b)
{
// do stuff with b
}
执行此操作时,GCC与
B.mpp:7:7: error: cannot declare ‘class B’ in a different module
7 | class B {};
| ^
In file included from B.mpp:3:
B_decl.h:1:7: note: declared here
1 | class B;
解决方案
此问题的解决方案取决于您首先要转发声明的原因。
如果您这样做是为了打破循环依赖,那么通常的解决方案是将它们放在同一个模块中。由于这些组件紧密耦合在一起,因此将它们放在同一模块中是有意义的。
如果您这样做是为了加快编译速度,那么最好简单地导入模块并使用类型。进口一个模块几乎没有成本。编译该模块,并且只编译一次。
相关文章