解决模板类之间的循环依赖关系
我有两个类,Foo<T>
和Bar<T>
,源自Base
。每种方法都会覆盖方法virtual Base* convert(ID) const
,其中ID
是唯一标识Foo
或Bar
(假设它是enum
)的特定实例化类型的实例。问题是Foo::convert()
需要能够返回一个Bar
实例,同样Bar::convert()
需要能够实例化Foo
。由于它们都是模板,因此会导致Foo.h
和Bar.h
之间的循环依赖关系。我该如何解决这个问题?解决模板类之间的循环依赖关系
编辑:预先声明不起作用,因为每种方法的实施需要其他类的构造函数:
Foo.h
:
#include <Base.h>
template<class T> class Bar;
template<class T>
class Foo : public Base { ... };
template<class T>
Base* Foo<T>::convert(ID id) const {
if (id == BAR_INT)
return new Bar<int>(value); // Error.
...
}
Bar.h
:
#include <Base.h>
template<class T> class Foo;
template<class T>
class Bar : public Base { ... };
template<class T>
Base* Bar<T>::convert(ID id) const {
if (id == FOO_FLOAT)
return new Foo<float>(value); // Error.
...
}
自然,错误是“不完整类型的无效使用”。
你需要做的是从实现中分离出类的声明。所以像
template <class T> class Foo : public Base
{
public:
Base* convert(ID) const;
}
template <class T> class Bar : public Base
{
public:
Base* convert(ID) const;
}
template <class T> Base* Foo<T>::convert(ID) const {return new Bar<T>;}
template <class T> Base* Bar<T>::convert(ID) const {return new Foo<T>;}
这样,当功能被定义时,你有完整的类定义。
这听起来很有希望。我在摆弄它。 – 2010-07-28 14:50:25
全套!非常感谢。 – 2010-07-28 14:55:49
花了我几个阅读来弄清楚你在做什么这个答案 - 通过将两个头文件合并在一起,并将函数定义放在两个类声明之下,以避免问题 - 好的想法。帮我也 – 2014-12-10 16:54:44
(已更新) 您应该能够处理与非模板类相同的内容。写下你的Bar.h像这样。 (同样地,对于foo.h中)
#if !defined(BAR_H_INCLUDED)
#define BAR_H_INCLUDED
template <class T>
class Foo;
template <class T>
class Bar
{
/// Declarations, no implementations.
}
#include "Foo.h"
template <class T>
Base* Bar<T>::Convert() { /* implementation here... */ }
#endif
没有骰子。这些类不能被提前声明,因为我需要使用它们的成员,或者至少是构造函数来执行转换。我得到了预期的“不完整类型的无效使用”。 – 2010-07-28 14:31:50
@Jon:查看更新后的帖子。 – 2010-07-28 14:53:58
我从KeithB的答案中得出的解决方案与此类似,但我不认为这是真正的编译,因为'Foo.h'和'Bar.h'仍然需要包含另一个,所以会出现空的-handed。 – 2010-07-28 14:58:43
你应该要么头
template <class T>
class X;
是非常好的模板类向前声明使用模板类向前声明。
James Curran的回答是天赐之物。一般来说,James的想法是限制包含所需的头文件,直到需要包含头文件的成员('声明)时为止。举个例子:
t1.hh
#ifndef S_SIGNATURE
#define S_SIGNATURE
struct G; // forward declaration
template<typename T>
struct S {
void s_method(G &);
};
#include "t2.hh" // now we only need G's member declarations
template<typename T>
void S<T>::s_method(G&g) { g.g_method(*this); }
#endif
t2.hh
#ifndef G_SIGNATURE
#define G_SIGNATURE
template<typename T>
struct S; // forward declaration
struct G {
template<typename T>
void g_method(S<T>&);
};
#include "t1.hh" // now we only need S' member declarations
template<typename T>
void G::g_method(S<T>& s) { s.s_method(*this); }
#endif
t.cc
#include "t1.hh"
#include "t2.hh"
S<int> s;
G g;
int main(int argc,char**argv) {
g.g_method(s); // instantiation of G::g_method<int>(S<int>&)
}
我只需要upvote一个答案开始“詹姆斯柯兰的答案是天赐之物” – 2013-11-13 22:58:19
循环依赖很少是一个好主意。尝试重构它,以便依赖关系被破坏。第一个想法是将'convert'方法转换为一个依赖于Bar和Foo的自由函数... – 2010-07-28 15:17:15