使用模板 #include 递归

#include recursion with template

提问人:NanzeRT 提问时间:2/15/2022 最后编辑:NanzeRT 更新时间:2/15/2022 访问量:188

问:

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

法典:

#pragma once
#include "B.h"

struct A
{
    B b;
    void doThingA() {}
};

A.K.(英语:A.K)

#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包括 from 但需要 模板函数实现 不。由于模板的原因,我也无法实现。A.hA.hB.h.cpp

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

C++ 模板 cmake linker-errors

评论

1赞 Aconcagua 2/15/2022
为什么这两种类型如此紧密地联系在一起?将两者放在同一个标题中会更有意义吗?
0赞 NanzeRT 2/15/2022
@Aconcagua在实际项目中,我正在做类似Unity的游戏引擎,其中是类,是有方法的。我正在用作各种系统的容器和链接器,因此其中许多都引用了 .AGameEngineBEntityAdd|GetComponent<T>()GameEngineGameEngine

答:

1赞 Stephen Newell 2/15/2022 #1

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

#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"

如果要为自己的组织拆分 / 为多个标头,一个简单的解决方案是添加编译时检查,以确保文件不会直接包含在内。AB

// 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

0赞 NanzeRT 2/15/2022 #2

根据 Stephen 的解决方案,我想出了这样的架构:

#pragma once
#define A_H
#include "B.h"

struct A
{
    B b;
    void doThingA() {}
};

#include "B_impl.h"

A.K.(英语:A.K)

#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