2017-07-30 107 views
0

通常在C++中,當我需要類之間的依賴關係時,我在頭文件中使用前向聲明,然後在每個cpp文件中包含兩個頭文件。如何在C++中聲明/定義相互依賴的模板?

但是,使用模板時此方法會中斷。因爲模板必須完全位於頭文件中(不包括我將代碼放入cpp併爲每個支持的T枚舉template class A<T>;的情況 - 這並不總是可行的,例如當T是lambda時)。

那麼有沒有辦法在C++中聲明/定義相互依存的模板?

代碼示例

template<typename T> struct B; 
template<typename T> struct A { 
    void RunA(B<T> *pB) { 
    // need to do something to B here 
    } 
}; 

template<typename T> struct B { 
    void RunB(A<T> *pA) { 
    // need to do something to A here 
    } 
}; 

如果我開始在RunA()做一些B,我想,我會得到一個「失蹤定義」錯誤,因爲B的只能向前聲明可以通過RunA()編譯的時間。

也許有一些技巧來組織頭文件例如通過將每個頭分成類定義和方法定義文件,然後以某種奇特的方式包含它們。或者也許可以通過第三/第四課來完成。但我無法想象如何專門做到這一點。

C++ 11/14/17都可以(特別是,它是MSVC++ 2017,工具集v141)。

+0

由於VS15能夠安裝3個不同的編譯器版本(以前所有版本都只有一個版本),因此請列出版本的年份,而不是列出編譯器的年份。 – tambre

+0

@SergeyB。,這解決了我給出的代碼示例的特定問題。但是,我想要一個像「不需要.h和.cpp文件一樣的通用指南,需要第三種文件類型的模板,如.inl,它將包含函數定義」,然後解釋如何管理include。 ... –

+0

大概就可以使部件的製造方法的功能模板,並通過'static_assert'喜歡強制類型:'模板<模板類C> 空隙RUNA(C * PB { static_assert(標準:: is_same ,B > :: value,「!」); // ... }'。這樣你只需要在調用點爲'B'定義。它可以爲你工作嗎? – skypjack

回答

1

您可以使用細粒度頭:

// A.forward.hpp 
template<typename T> struct A; 

// A.decl.hpp 
#include "A.forward.hpp" 
#include "B.forward.hpp" 

template<typename T> struct A 
{ 
    void RunA(B<T> *pB); 
}; 

// A.impl.hpp 
#include "A.decl.hpp" 
#include "B.hpp" 

template<typename T> void A<T>:: 
RunA(B<T> *pB) 
{ 
    // need to do something to B here 
} 

// A.hpp // this one should be included by code using A 
#include "A.decl.hpp" 
#include "A.impl.hpp" 

// B.forward.hpp 
template<typename T> struct B; 

// B.decl.hpp 
#include "B.forward.hpp" 
#include "A.forward.hpp" 

template<typename T> struct B 
{ 
    void RunB(A<T> *pA); 
}; 

// B.impl.hpp 
#include "B.decl.hpp" 
#include "A.hpp" 

template<typename T> void B<T>:: 
RunB(A<T> *pA) 
{ 
    // need to do something to A here 
} 

// B.hpp // this one should be included by code using B 
#include "B.decl.hpp" 
#include "B.impl.hpp" 

顯然,所有這些頭也需要某種形式的頭警衛,我省略這裏。 重要通知.impl.hpp標題被認爲是內部的,不應該被外部代碼使用,而.forward.hpp,.decl.hpp.hpp可以在任何地方使用。

該方法確實引入了循環包含依賴關係,但這並不會導致問題:通過包含頭文件生成的代碼結構確保代碼部分按以下順序包含:前向聲明,類定義,方法定義。

+0

這個設計中有一個循環包含:A.hpp - > A.impl.hpp - > B.hpp - > B.impl.hpp - > A.hpp –

+1

@SergeRogatch是的,但是A.hpp不會是包括第二次,這不會導致問題。如果只包含A.hpp,只包含B.hpp,或者兩者都以任意順序包含,它都可以正常工作。你應該自己嘗試一下。顯然,所有這些頭文件也需要某種類型的頭文件,我在這裏省略。重要提示:.decl.hpp和.impl.hpp標頭被認爲是內部的,不應該被外部代碼使用,而.forward.hpp和.hpp可以在任何地方使用。 – VTT

+0

這是值得在你的答案描述。另外,這是一些標準/最佳做法嗎?或者你有沒有想出這個解決方案?我想知道有多大的機會有更好的解決方案... –

0

您可以分開類的聲明和定義。因此,您可以分開模板類的聲明和定義...

您可以分開類方法的聲明和定義。因此,你可以單獨的模板類方法的聲明和定義:

template<typename T> struct B;  // declaration 

template<typename T> struct A {  // definition 
    void RunA(B<T> *pB); // declaration 
}; 

template<typename T> struct B {  // definition 
    void RunB(A<T> *pA); // declaration 
}; 

// definition 
template<typename T> 
void A<T>::RunA(B<T> *pB) { 
    // need to do something to B here 
    } 

// definition  
template<typename T> 
void B<T>::RunB(A<T> *pA) { 
    // need to do something to A here 
    } 
+0

這很清楚。但實際上,將所有內容放在一個文件中很不方便。問題是如何將這些聲明/定義分解爲頭文件以及如何包含它們。 –

+1

@SergeRogatch與.hpp和.cpp相同。規範是爲模板函數定義使用擴展.ipp。 –