C++ 模块从另一个模块转发声明实体

问题描述

我最近一直在尝试使用 GCC 11 将代码库转换为 C++20 模块。但是,我遇到了以下情况。首先,这是如何使用标头完成的:

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。所以,我的主要问题是:我怎样才能从模块外部的模块中转发声明某些东西?我也会对最终产生相同效果的方式感到满意:A 的用户不会B变化时需要重新编译。

搜索的过程中,我发现有几个地方在谈论类似的情况,但由于某种原因它们都不起作用。它们不起作用的原因:

  • 有人说有一个带有前向声明的模块。正如我上面所说,这行不通。
  • 有人说使用声明的所有权声明。但是,它们已从最终的 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:在header中向前声明,在模块中定义

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;

解决方法

对此的解决方案取决于您首先要转发声明的原因。

如果您这样做是为了打破循环依赖,那么通常的解决方案是将它们简单地放在同一个模块中。由于组件如此紧密地耦合在一起,因此将它们放在同一个模块中是有意义的。

如果你这样做是为了加快编译速度,你最好简单地导入模块并使用类型。导入模块几乎没有成本。编译模块有,而且只做了一次。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...