2010-07-28 133 views
16

我有两个类,Foo<T>Bar<T>,源自Base。每种方法都会覆盖方法virtual Base* convert(ID) const,其中ID是唯一标识FooBar(假设它是enum)的特定实例化类型的实例。问题是Foo::convert()需要能够返回一个Bar实例,同样Bar::convert()需要能够实例化Foo。由于它们都是模板,因此会导致Foo.hBar.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. 

    ... 

} 

自然,错误是“不完整类型的无效使用”。

+0

循环依赖很少是一个好主意。尝试重构它,以便依赖关系被破坏。第一个想法是将'convert'方法转换为一个依赖于Bar和Foo的自由函数... – 2010-07-28 15:17:15

回答

18

你需要做的是从实现中分离出类的声明。所以像

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

这样,当功能被定义时,你有完整的类定义。

+0

这听起来很有希望。我在摆弄它。 – 2010-07-28 14:50:25

+0

全套!非常感谢。 – 2010-07-28 14:55:49

+0

花了我几个阅读来弄清楚你在做什么这个答案 - 通过将两个头文件合并在一起,并将函数定义放在两个类声明之下,以避免问题 - 好的想法。帮我也 – 2014-12-10 16:54:44

9

(已更新) 您应该能够处理与非模板类相同的内容。写下你的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 
+0

没有骰子。这些类不能被提前声明,因为我需要使用它们的成员,或者至少是构造函数来执行转换。我得到了预期的“不完整类型的无效使用”。 – 2010-07-28 14:31:50

+0

@Jon:查看更新后的帖子。 – 2010-07-28 14:53:58

+0

我从KeithB的答案中得出的解决方案与此类似,但我不认为这是真正的编译,因为'Foo.h'和'Bar.h'仍然需要包含另一个,所以会出现空的-handed。 – 2010-07-28 14:58:43

11

你应该要么头

template <class T> 
class X; 

是非常好的模板类向前声明使用模板类向前声明。

7

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>&) 
} 
+0

我只需要upvote一个答案开始“詹姆斯柯兰的答案是天赐之物” – 2013-11-13 22:58:19