Can C++ modules in Visual Studio 2022 handle forward declarations?
The following code has a circular dependency that I want to break using a forward declaration, but the compiler doesn’t seem to recognize that it refers to the same type as the full class declaration, so it doesn’t compile. Am I doing something wrong?
main.cpp
import A;
int main()
{
CB cb;
CC cc;
cc.MyFunc3(cb);
return 0;
}
A.ixx
export module A;
export import :B;
export import :C;
A-B.ixx
export module A:B;
class CC;
export class CB {
public:
void MyFunc(const CC& cc) const;
};
A-B.cpp
module A;
import :B;
import std;
import :C;
void CB::MyFunc(const CC& cc)
{
std::cout << "Hello World B" << std::endl;
}
A-C.ixx
export module A:C;
class CB;
export class CC {
public:
void MyFunc1(const CB& cb) const;
};
A-C.cpp
module A;
import :C;
import std;
import :B;
void CC::MyFunc1(const CB& cb)
{
std::cout << "Hello World B" << std::endl;
cb.MyFunc(*this);
}
This is the error:
1>C:UserspeterBrepositorieshobby projectenHexanaut-AIHexanaut-AIHexanaut-AIA-C.cpp(7,10): error C2511: 'void CC::MyFunc1(const CB &)': overloaded member function not found in 'CC'
1>C:UserspeterBrepositorieshobby projectenHexanaut-AIHexanaut-AIHexanaut-AIA-C.ixx(5,14):
1>see declaration of 'CC'
1>C:UserspeterBrepositorieshobby projectenHexanaut-AIHexanaut-AIHexanaut-AIA-C.cpp(10,5): error C2027: use of undefined type 'CB'
1>C:UserspeterBrepositorieshobby projectenHexanaut-AIHexanaut-AIHexanaut-AIA-C.ixx(3,7):
1>see declaration of 'CB'
1>C:UserspeterBrepositorieshobby projectenHexanaut-AIHexanaut-AIHexanaut-AIA-C.cpp(10,16): error C2671: 'CC::MyFunc1': static member functions do not have 'this' pointers
Relevant compiler options:
/std:c++latest /experimental:module
With ‘Build ISO C++23 standard library modules’ turned on.
2
Answers
Two key things were missing: exporting the forward declaration but also importing them into the module implementation units. I got it working as well with module implementation units for the partitions. Here is my full solution:
main.cpp
A-fwddecl.ixx
A.ixx
A-B.ixx
A-B.cpp
A-C.ixx
A-C.cpp
If a partition ever needs something which is declared in the A module itself, just
import A;
in the module implementation unit of the partition.You need to be more consistent about your
export
declarations. This includes forward declarations.CC
andCB
are both supposed to beexport
ed. This means that the forward declarations of them should also beexported
.One way to avoid excess repetition is to have a module interface partition for such declarations that all other partitions import:
The
export
inA-B
is not necessary, but it’s good as a reminder.A-C.ixx
should also import:fwd
.Note that my reading of the standard is that it should have given a compile error when compiling the module
A
. That file importsA:B
, which declaresCC
as non-exported. When it importsA:C
that declares it exported, that should have triggered [module.interface]/6:That "shall not be exported" translates to "ill-formed if the entity is redeclared as exported". But VC++ didn’t do that apparently.
It does give an error eventually, but only when you try to use it. See, it seems to be translating your code such that there are two types named
CB
: the exported one and the non-exported one. The objectcb
is of the exported type, but the type used byCC::MyFunc3
is the non-exported one. Since they are two different types, and no conversion operator is defined, you get an error.