我有以下架构问题导致标头的循环依赖。它涉及通用编程和C ++模板。所以:
以下是一些代码来说明这一切:
// 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;
}
答案 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;
};
在编译器需要实例化它们之前, Property
和ConcreteNodeA
无需完成。如果您需要它们是完整的,例如因此,您可以在ConcreteNodeB.hpp中的内联声明中使用m_nodeA
,然后您可以随时包含Property.hpp,并在 ConcreteNodeB类声明之后包含ConcreteNodeA.hpp 。