模板,循环依赖,方法,哦,我的!

时间:2010-07-05 04:31:01

标签: c++ templates circular-dependency

背景:我正在研究基于现有Java类模型生成C ++代码的framework。因此我无法改变下面提到的循环依赖。

假设:

  • 父子关系
  • 父级包含子级列表
  • 用户必须能够在运行时
  • 查找列表元素类型

我在以下测试用例中对此进行了建模:

Main.cpp的

#include "Parent.h"

#include <iostream>
using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
    Parent parent;
    cout << Parent::getType() << endl;
    cout << parent.getChildren().getType() << endl;
    return 0;
}

Parent.h

#ifndef PARENT_H
#define PARENT_H

#include <string>

#include "Array.h"
class Child;

class Parent
{
public:
    Array<Child> getChildren()
    {
        return Array<Child>();
    }

    static std::string getType()
    {
        return "parent";
    }
};

#endif

Child.h

#ifndef CHILD_H
#define CHILD_H

#include "Parent.h"

class Child: public Parent
{
};

#endif

Array.h

template <typename ElementType>
class Array
{
public:
    static std::string getType()
    {
        return ElementType::getType();
    }
};
  1. 当我编译上面的代码时,我得到: error C2027: use of undefined type 'Child'

  2. return ElementType::getType();
  3. 如果我尝试#include "Child.h"而不是前方声明我得到: error C2504: 'Parent' : base class undefined

  4. class Child: public Parent
  5. 如果我尝试Array<Child*>代替Array<Child>,我会: error C2825: 'ElementType': must be a class or namespace when followed by '::'

  6. return ElementType::getType();

    循环依赖是因为:

    1. Child.h需要了解类Parent
    2. Parent.h需要了解类Array
    3. Array.h需要了解类Child
    4. 有什么想法吗?

5 个答案:

答案 0 :(得分:4)

解决此问题的一种方法是将实现与接口分开。

因此,将Parent的实现放入.cpp文件中,以便编译器在编译Parent :: getChildren()时可以看到Parent,Child和Array的定义。

#ifndef PARENT_H
#define PARENT_H
#include <string>
#include "Array.h"

class Child;

class Parent
{
public:
    Array<Child> getChildren();
    static std::string getType();
};

#endif

在parent.cpp中:

#include "parent.hpp"
#include "child.hpp"

Array<Child> Parent::getChildren() {
    return Array<Child>();
}

// etc.

更新

是的,实际问题是由Array :: getType()实例化而没有Child存在的定义,因此我的解决方案不完整。

Pete Kirkham的解决方案很好:只需将child.hpp包含在main中。

要使接口/实现分离工作,需要一个单独的Array实现文件,并显式实例化Array和任何其他所需的实例化。这可能不是你想要的,但为了完整性,它看起来像是:

在array.hpp中:

#ifndef ARRAY_HPP
#define ARRAY_HPP

#include <string>

template <typename ElementType>
class Array
{
public:
    static std::string getType();
};

#endif

在array.cpp中:

#include "array.hpp"
#include "child.hpp"

template<typename ElementType>
std::string Array<ElementType>::getType()
{
    return ElementType::getType();
}

template class Array<Child>;

答案 1 :(得分:4)

错误是由于在实例化模板时Child类不存在。

将以下内容添加到Main.h或Parent.h的 end

#include "Child.h"

这适用于g ++ 4和VS 2010。

答案 2 :(得分:1)

编辑: OP编辑了问题,以消除我和rlbond注意到的无限大小数据结构问题。通过此更改,现在可以使用Array<Child>代替Array<Child*>,而janm's answer显示。

Array<Child>更改为Array<Child*>更改Array类型,以了解它包含指向对象的指针,而不是对象本身:

新的Array.h

// E.g. strip_pointer_from<Foo*>::type is Foo
template <typename T>
struct strip_pointer_from<T> {};

template <typename T>
struct strip_pointer_from<T*> {
    typedef T type;
};

template <typename ElementType>
class Array
{
public:
    static std::string getType()
    {
        return typename strip_pointer_from<ElementType>::type::getType();
    }
};

我强烈建议重新考虑Array,但有没有办法可以使用普通vector<Child>并只询问每个元素的类型?

答案 3 :(得分:0)

也许您可以使用指向Child中的Child的指针? 像

这样的东西
#ifndef PARENT_H
#define PARENT_H

#include <string>

#include "Array.h"
class Child;

class Parent
{
public:
    Array<Child*> getChildren()
    {
        return Array<Child*>();
    }

    static std::string getType()
    {
        return "parent";
    }
};

#endif

或者,更一般地说,也许可以使用编译器防火墙技术(a.k.a.不透明指针,a.k.a。PIMLP)。有关它的更多信息here

答案 4 :(得分:0)

以下是我倾向于在需要元类信息的系统中解决问题的方法。另请参阅其他答案,以便更直接地解决您的问题。


stl中使用的模式是使用类型来表示类型,而不是字符串。所以std::vector<T>::value_type表示存储在向量中的类型。然后由客户端代码来使用这种类型。

如果需要对象的运行时类型,请使用具有返回类型的虚函数的基类。对于场所的静态类型,您可以使用部分专业化:

Object.h

#ifndef OBJECT_H
#define OBJECT_H

#include <string>

template <typename T>
struct type_info {
    // extend type_info<void*> to inherit defaults
    const static bool is_array = false;
};

template <typename T>
std::string type_name ( const T& )
{
    return type_info<T>::name();
};

template <typename T>
std::string type_name ()
{
    return type_info<T>::name();
};

#endif

Parent.h

#include "Object.h"
#include "Array.h"

class Child;
class Parent
{
public:
    Array<Child> getChildren() {
        return Array<Child>();
    }
};

template <>
struct type_info <Parent> : public type_info<void*> {
    static std::string name () {
        return "parent";
    }
};


#endif

Array.h

template <typename ElementType>
class Array
{
public:
    typedef ElementType value_type;
};

template <typename T>
struct type_info <Array<T > > {
    static std::string name () {
        return "Array<" + type_name<T>() + ">";
    }

    const static bool is_array = true;
};

Child.h

#ifndef CHILD_H
#define CHILD_H

#include "Parent.h"

class Child: public Parent
{
};

template <>
struct type_info <Child> : public type_info<void*> {
    static std::string name () {
        return "child";
    }
};

#endif

Main.cpp的

#include "Object.h"
#include "Parent.h"
#include "Child.h"

#include <iostream>
#include <iomanip>

using std::cout;
using std::endl;
using std::boolalpha;

template<typename T> bool type_is_array (const T&) { return type_info<T>::is_array; }

int main(int argc, char* argv[])
{
    Parent parent;
    cout << type_name<Parent>() << endl;
    cout << type_name(parent.getChildren()) << endl;
    cout << boolalpha << type_is_array(parent) << endl;
    cout << boolalpha << type_is_array(parent.getChildren()) << endl;
    return 0;
}