循环依赖中的 Typedef 类

Typedefs in circular-dependend classes

我有几个 classes (A, B, C) 每个都有一个模板化 (Ptr<...>) 类型的成员关于另一个class(循环)的不完整类型。我想对类型 (::ptr) 进行类型定义,如下所示。这似乎不起作用——我的编译器告诉我以下内容:

In file included from B.hpp:6:0,
                 from A.hpp:6:
C.hpp:13:8: error: ‘ptr’ in ‘class A’ does not name a type
     A::ptr a;
        ^

但是使用 T* 而不是 T::ptr 可以使它正常工作。我该如何解决?

A.hpp:

#ifndef TEST_INCLUDE_A
#define TEST_INCLUDE_A 1

class A;

#include "B.hpp"

#include "P.hpp"

class A {
public:
    typedef Ptr<A> ptr;
    B::ptr b;
};

#endif

B.hpp:

#ifndef TEST_INCLUDE_B
#define TEST_INCLUDE_B 1

class B;

#include "C.hpp"

#include "P.hpp"

class B {
public:
    typedef Ptr<B> ptr;
    C::ptr c;
};

#endif

C.hpp:

#ifndef TEST_INCLUDE_C
#define TEST_INCLUDE_C 1

class C;

#include "A.hpp"

#include "P.hpp"

class C {
public:
    typedef Ptr<C> ptr;
    A::ptr a;
};

#endif

P.hpp:

#ifndef TEST_INCLUDE_PTR
#define TEST_INCLUDE_PTR 1

template<class T>
class Ptr {
public:
    T* ptr_t;
};

#endif

有些操作只能对完整类型执行。其中之一是,来自 [basic.def.odr]:

A class type T must be complete if:
— [...]
— a class member access operator is applied to an expression of type T (5.2.5), or
— [...]

写入 A::ptr 需要 A 才能完成。 A 在我们定义 C 的地方不完整,所以这是一个错误。

另一方面,当您写 A* 时, 而不是 需要 A 才能完成。将不完整类型的指针(或引用)作为成员就可以了。

我的建议:

  1. 稍微更新 P 的定义以定义派生类型名称。

    template<class T>
    class Ptr {
    public:
        using ptr_t = T*;
        ptr_t ptr;
    };
    
  2. 更新 A.hpp、B.hpp 和 C.hpp 以仅依赖 BCA, 分别.

    A.hpp的更新版本。

    #ifndef TEST_INCLUDE_A
    #define TEST_INCLUDE_A 1
    
    #include "P.hpp"
    
    class B;
    
    class A {
       public:
          typedef Ptr<A> ptr;   // This does not seem useful any longer
                                // It can probably be removed.
          Ptr<B> b_ptr;
    };
    
    #endif
    

    类似地更新B.hpp和C.hpp。

为了解决循环依赖,您只需要帮助编译器并预知 ptr 将在另一个 class 中发生什么,即:你知道A::ptrPtr<A>,依此类推。

online demo

class A;
class B;

template<typename T>
struct Ptr { T* ptr_t; };

class A {
public:
    using ptr = Ptr<A>;
    Ptr<B> b;
};

class B {
public:
    using ptr = Ptr<B>;
    Ptr<A> a;
};

int main() {
    A a;
    B b;
    a.b.ptr_t = &b;
    b.a.ptr_t = &a;

    A::ptr aptr;
    B::ptr bptr;
    aptr.ptr_t = &a;
    bptr.ptr_t = &b;
    a.b = bptr;
    b.a = aptr;
}