2010-07-28 132 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