模板和循环依赖

时间:2013-12-22 15:17:02

标签: c++ templates generic-programming

我有以下架构问题导致标头的循环依赖。它涉及通用编程和C ++模板。所以:

  • 我已经定义了在节点中聚合的属性
  • 这些属性定义为模板
  • 此模板的一个参数是属性
  • 处理的数据类型
  • 每个类型系列都有一个Property模板的特化:
    • 基本类型(float,int ...)
    • 指向节点的指针
  • 使用元编程计算数据类型的族
    • 基本类型 - > template argument = 0
    • 指向节点的指针 - > template argument = 1
    • 未处理的类型 - > template argument = -1
  • 我的循环依赖关系与类型系列“指向节点的指针”有关
    • 为了确定指针的类型是否是一个节点我有编译时代码来检查类ConcreteNode是否来自类Node
    • 虽然要确定这一点,编译器需要知道ConcreteNode的声明
    • 因此,当定义包含指向ConcretNode的指针的属性而不是简单地向前声明ConcreteNode
    • 时,我必须包含ConcreteNode的头。
  • 如果ConcreteNodeA包含指向ConcreteNodeB的指针,则会导致问题,而反向混凝土NodeNodeB包含指向ConcreteNodeA的指针
    • 我是这种情况我需要在ConcreteNodeA的标题中包含ConcreteNodeB的标题,并且反向
    • 这会导致标题的循环依赖

以下是一些代码来说明这一切:

// Property.hpp
class PropertyBase
{
    // Some code common to all properties.
}

template <class T, int typeFamily = TypeFamily<T>::value >
class Property : public PropertyBase
{
    // Default template to catch errors.
};

template <class T>
class Property<T, 0> : public PropertyBase
{
    // Data is a base type.
    T* m_dataRef;

    // Some basic types-specific stuff.
};

template <class T> 
class Property<T*, 1> : public PropertyBase
{
    // Data is a pointer to a concrete node.
    T** m_dataRef;

    // Some pointer to node-specific stuff.
}; 


// ConcreteNodeA.h
#include "ConcreteNodeB"

class ConcreteNodeA : public Node
{
protected:
    Property<ConcreteNodeB*>& m_nodeB;
};


// ConcreteNodeB.h
#include "ConcreteNodeA"

class ConcreteNodeB : public Node
{
protected:
    Property<ConcreteNodeA*>& m_nodeA;
};

显然我的真实代码比这更复杂:

  • 即。只有当它是处理类型列表的一部分时才处理基类型
  • 我没有公开用于确定某个类是否派生出另一个类的元代码
    • 我猜人们愿意帮助我了解它
    • 那些不这样做的人不应该被它包围

任何人都知道如何在不出现这种循环依赖问题的情况下声明我的属性?目前我被卡住了......

谢谢!

-----编辑------

以下

TypeFamily<T>::value

是元代码,如果T是受支持的基类型,则返回0;如果T是指向从节点派生的类的指针,则返回1;在其他情况下,返回-1。

-----编辑------

我不能使用ConcreteNodeA和ConcreteNodeB的前向声明,因为在这种情况下,编译器无法“理解”ConcreteNodeA和ConcreteNodeB派生自Node。因此TypeFamily :: value将返回-1,而不是1.因此,所选的模板将是默认的“catch-errors”,而不是我想要的(typeFamily == 1)。

-----编辑------

Node了解Property,因为它汇总了它们。它看起来像这样:

#include "Property.hpp" // This file also contains the declaration for PropertyBase.

class Node
{
private:
    std::vector<PropertyBase*> m_props;
}

2 个答案:

答案 0 :(得分:3)

改为使用前向声明:

ConcreteNodeA.h:

class ConcreteNodeB;

class ConcreteNodeA : public Node
{
protected:
    Property<ConcreteNodeB*>& m_nodeB;
};

ConcreteNodeB.h:

class ConcreteNodeA;

class ConcreteNodeB : public Node
{
protected:
    Property<ConcreteNodeA*>& m_nodeA;
};

答案 1 :(得分:1)

与计算机科学中的每个问题一样,这可以通过添加另一层间接来解决。问题的关键在于确定Property的第二个默认参数的类型特征要求第一个参数的类型完整。如果该类型不完整,您甚至不能声明指向Property的指针或引用,否则Property本身甚至不需要完成。解决方案是通过引入中间层类T来延迟对类型特征PropertyImplementation的分析,直到实例化时间:

template <class T, int typeFamily = TypeFamily<T>::value >
class PropertyImplementation : public PropertyBase
{
    // Default template to catch errors.
};

template <class T>
class PropertyImplementation<T, 0> : public PropertyBase
{
    // Data is a base type.
    T* m_dataRef;

    // Some basic types-specific stuff.
};

template <class T>
class PropertyImplementation<T*, 1> : public PropertyBase
{
    // Data is a pointer to a concrete node.
    T** m_dataRef;

    // Some pointer to node-specific stuff.
};

template <class T>
class Property : public PropertyImplementation<T> {};

现在您可以使用不完整类型声明Property作为参数,实际上您甚至可以转发声明Property本身:

// ConcreteNodeB.hpp

#include "Node.hpp"

class ConcreteNodeA;
template <class> class Property;

class ConcreteNodeB : public Node
{
protected:
    Property<ConcreteNodeA*>& m_nodeA;
};
在编译器需要实例化它们之前,

PropertyConcreteNodeA无需完成。如果您需要它们是完整的,例如因此,您可以在ConcreteNodeB.hpp中的内联声明中使用m_nodeA,然后您可以随时包含Property.hpp,并在 ConcreteNodeB类声明之后包含ConcreteNodeA.hpp