#使用模板包含递归



我有这样的问题为什么模板只能在头文件中实现?(还有用Template类构造基于CMake的项目的正确方法(,但使用include递归。

代码:

A.h

#pragma once
#include "B.h"
struct A
{
B b;
void doThingA() {}
};

B.h

#pragma once
struct A;
struct B
{
A *a;
template<typename T>
void doThingB();
};
#include "A.h"
template<typename T>
void B::doThingB()
{
a->doThingA();
}

main.cpp

#include "A.h"
int main() {}

错误:

In file included from A.h:2,
from main.cpp:1:
B.h: In member function 'void B::doThingB()':
B.h:16:6: warning: invalid use of incomplete type 'struct A'
16 |     a->doThingA();
|      ^~
B.h:2:8: note: forward declaration of 'struct A'
2 | struct A;
|        ^

B.h包括来自A.h的,但B.h中的模板函数实现所需的A.h不包括。由于模板的原因,我也无法将实现带到.cpp

可以通过使用模板的显式实例化来解决,但我想知道另一种解决方案。

当您有这样紧密耦合的类型时,最简单的解决方案是将它们放在单个头文件中。

#pragma once
// forward declaration of A
struct A;
// declare B, since A needs it
struct B
{
A *a;
template<typename T>
void doThingB();
};
// now we can declare A
struct A
{
B b;
void doThingA() {}
};
// and finally, implement the parts of B that need A
// and need to be in the header
template<typename T>
void B::doThingB()
{
a->doThingA();
}

如果你仍然想要B.h,那么它可以是单行:

#include "A.h"

如果您想为自己的组织将A/B拆分为多个标头,一个简单的解决方案是添加编译时检查,以确保不直接包含这些文件。

// A.h
#pragma once
#define A_IMPL
#include "B_impl.h"
#undef A_IMPL
// B.h
#pragma once
#ifndef A_IMPL
#error "B_impl.h can't be included directly; use A.h"
#endif
struct A;
struct B
{
A *a;
template<typename T>
void doThingB();
};
#include "A_impl.h"
template<typename T>
void B::doThingB()
{
a->doThingA();
}
// A_impl.h
#pragma once
#ifndef A_IMPL
#error "A_impl.h can't be included directly; use A.h"
#endif
struct A
{
B b;
void doThingA() {}
};

如果您希望更多的头直接使用impl文件,可以使#ifdef检查更加复杂,但公共接口仍然没有意识到这一点。

基于Stephen的解决方案,我提出了架构:

A.h

#pragma once
#define A_H
#include "B.h"
struct A
{
B b;
void doThingA() {}
};
#include "B_impl.h"

B.h

#pragma once
struct A;
struct B
{
A *a;
template <typename T>
void doThingB();
};
#ifndef A_H
#include "B_impl.h"
#endif

B_impl.h

#pragma once
#include "A.h"
template <typename T>
void B::doThingB()
{
a->doThingA();
}

main.cpp

#include "A.h"
int main() {}

所以我可以分别包括A.hB.h,而且它对我来说看起来更干净

最新更新