模板、循环依赖、方法,天哪!

发布于 2024-09-08 19:45:16 字数 1891 浏览 5 评论 0原文

背景:我正在开发一个框架,它基于现有的 Java 类模型。因此,我无法更改下面提到的循环依赖关系。

给定:

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

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

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

数组。 h

template <typename ElementType>
class Array
{
public:
    static std::string getType()
    {
        return ElementType::getType();
    }
};
  1. 当我编译上面的代码时,我得到: 错误 C2027:在 return ElementType::getType();

    处使用未定义类型“Child”
  2. 如果我尝试#include "Child.h"< /code> 而不是我得到的前向声明: 错误 C2504: 'Parent' : 基类未定义 at class Child: public Parent

  3. 如果我尝试 ArrayArray 我得到: 错误 C2825:“ElementType”:在 return ElementType::getType();

    处后跟“::”时必须是类或命名空间;

循环依赖的产生是因为:

  1. Child.h 需要了解 Parent 类
  2. Parent.h 需要了解 Array 类
  3. Array.h 需要了解 Child 类

有什么想法吗?

Background: I am working on a framework that generates C++ code based on an existing Java class model. For this reason I cannot change the circular dependency mentioned below.

Given:

  • A Parent-Child class relationship
  • Parent contains a list of Children
  • Users must be able to look up the list element type at run-time

I've modeled this in the following testcase:

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. When I compile the above code I get:
    error C2027: use of undefined type 'Child' at return ElementType::getType();

  2. If I try #include "Child.h" instead of the forward declaration I get:
    error C2504: 'Parent' : base class undefined at class Child: public Parent

  3. If I try Array<Child*> instead of Array<Child> I get:
    error C2825: 'ElementType': must be a class or namespace when followed by '::' at return ElementType::getType();

The circular dependency comes about because:

  1. Child.h needs to know about class Parent
  2. Parent.h needs to know about class Array
  3. Array.h needs to know about class Child

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

↙温凉少女 2024-09-15 19:45:16

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

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

#include "Child.h"

这可以在 g++ 4 和 VS 2010 中正常编译。

The error is due to the Child class not being present when the template is instantiated.

Add the following either to Main or at the end of Parent.h:

#include "Child.h"

This compiles fine with both g++ 4 and VS 2010.

乞讨 2024-09-15 19:45:16

解决这个问题的一种方法是将实现与接口分离。

因此,将Parent的实现放入.cpp文件中,以便编译器在编译Parent::getChildren()时可以看到Parent、Children和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.

更新:

是的,实际问题是由于在没有存在Child定义的情况下实例化Array::getType()引起的,所以我的解决方案是不完整的。

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

One way to resolve this problem is to separate the implementations from the interfaces.

So, put the implementation of Parent into a .cpp file, so that the compiler can see the definitions of Parent, Child and Array when compiling Parent::getChildren().

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

class Child;

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

#endif

And in parent.cpp:

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

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

// etc.

Update:

Yes, the actual problem is caused by Array::getType() being instantiated without a definition of Child being present, so my solution is incomplete.

Pete Kirkham's solution is good: just include child.hpp into main.

For a interface/implementation separation to work, a separate implementation file for Array would be required with explicit instantiation of Array and any other required instantiations. This is probably not what you want, but for completeness, it would look something like:

In array.hpp:

#ifndef ARRAY_HPP
#define ARRAY_HPP

#include <string>

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

#endif

And in array.cpp:

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

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

template class Array<Child>;
夏至、离别 2024-09-15 19:45:16

编辑: OP 编辑​​了这个问题,以消除我和 rlbond 注意到的无限大小数据结构问题。通过此更改,现在可以使用 Array 而不是 Array,如 janm 的回答显示。

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

New 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 并只询问每个元素的类型?

EDIT: The OP has edited the question to remove the infinite-size-data-structure problem noticed by myself and rlbond. With this change, it's now possible to use Array<Child> instead of Array<Child*>, as janm's answer shows.

Change Array<Child> to Array<Child*>, and alter the Array type to understand that it contains pointers to objects instead of the objects themselves:

New 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();
    }
};

I would strongly recommend rethinking Array, though -- is there any way you can use a plain vector<Child> and just ask each element for its type?

红焚 2024-09-15 19:45:16

也许你可以使用指向 Parent 中的 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

,更一般地说,也许可以使用编译器防火墙技术(又名不透明指针,又名 PIMLP)。有关它的更多信息此处

Maybe you could use pointer to Child in Parent instead?
Something like

#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

Or, more generally, maybe it is possible to use compiler firewall technique (a.k.a. opaque pointer, a.k.a. PIMLP). More info about it here.

谈场末日恋爱 2024-09-15 19:45:16

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


stl中使用的模式是用类型来表示类型,而不是字符串。因此 std::vector::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;
}

The following is how I've tended to solve the problem in systems requiring metaclass information. See also other answer for a more direct solution to your problem.


The pattern used in the stl is to use a type to represent a type, not a string. So std::vector<T>::value_type represents the type stored in the vector. It is then up to the client code to make use of this type.

If you want the runtime type of the object, use a base class which has a virtual function that returns the type. For the static type of a place, you can use partial specialisation:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文