模板、循环依赖、方法,天哪!
背景:我正在开发一个框架,它基于现有的 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();
}
};
当我编译上面的代码时,我得到:
错误 C2027:在
return ElementType::getType();
处使用未定义类型“Child”
如果我尝试
#include "Child.h"< /code> 而不是我得到的前向声明:
错误 C2504: 'Parent' : 基类未定义
atclass Child: public Parent
如果我尝试
Array
的Array
我得到:错误 C2825:“ElementType”:在
return ElementType::getType();
处后跟“::”时必须是类或命名空间;
循环依赖的产生是因为:
- Child.h 需要了解 Parent 类
- Parent.h 需要了解 Array 类
- 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();
}
};
When I compile the above code I get:
error C2027: use of undefined type 'Child'
atreturn ElementType::getType();
If I try
#include "Child.h"
instead of the forward declaration I get:error C2504: 'Parent' : base class undefined
atclass Child: public Parent
If I try
Array<Child*>
instead ofArray<Child>
I get:error C2825: 'ElementType': must be a class or namespace when followed by '::'
atreturn ElementType::getType();
The circular dependency comes about because:
- Child.h needs to know about class Parent
- Parent.h needs to know about class Array
- Array.h needs to know about class Child
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
该错误是由于实例化模板时不存在 Child 类。
将以下内容添加到 Main 或 Parent.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:
This compiles fine with both g++ 4 and VS 2010.
解决这个问题的一种方法是将实现与接口分离。
因此,将Parent的实现放入.cpp文件中,以便编译器在编译Parent::getChildren()时可以看到Parent、Children和Array的定义。
在parent.cpp中:
更新:
是的,实际问题是由于在没有存在Child定义的情况下实例化Array::getType()引起的,所以我的解决方案是不完整的。
Pete Kirkham 的解决方案很好:只需将 child.hpp 包含到 main 中即可。
为了实现接口/实现分离,需要一个单独的 Array 实现文件,并显式实例化 Array 和任何其他所需的实例。这可能不是您想要的,但为了完整性,它看起来像:
在 array.hpp 中:
在 array.cpp 中:
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().
And in parent.cpp:
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:
And in array.cpp:
编辑: OP 编辑了这个问题,以消除我和 rlbond 注意到的无限大小数据结构问题。通过此更改,现在可以使用
Array
而不是Array
,如 janm 的回答显示。将
Array
更改为Array
,并更改Array
类型以了解它包含指向对象而不是对象本身的指针:New Array.h
我强烈建议重新考虑
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 ofArray<Child*>
, as janm's answer shows.Change
Array<Child>
toArray<Child*>
, and alter theArray
type to understand that it contains pointers to objects instead of the objects themselves:New Array.h
I would strongly recommend rethinking
Array
, though -- is there any way you can use a plainvector<Child>
and just ask each element for its type?也许你可以使用指向 Parent 中的 Child 的指针?
或者
,更一般地说,也许可以使用编译器防火墙技术(又名不透明指针,又名 PIMLP)。有关它的更多信息此处。
Maybe you could use pointer to Child in Parent instead?
Something like
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.
以下是我倾向于解决需要元类信息的系统中的问题的方法。另请参阅其他答案,以更直接地解决您的问题。
stl中使用的模式是用类型来表示类型,而不是字符串。因此
std::vector::value_type
表示存储在向量中的类型。然后由客户端代码来使用这种类型。如果您想要对象的运行时类型,请使用具有返回该类型的虚函数的基类。对于位置的静态类型,您可以使用部分特化:
Object.h
Parent.h
Array.h
Child.h
Main.cpp
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
Parent.h
Array.h
Child.h
Main.cpp