2017-07-30 22 views
0

通常、C++ではクラス間の依存関係が必要なときに、ヘッダーファイルで前方宣言を使用し、両方のヘッダーファイルを各cppファイルに含めます。C++で相互依存型テンプレートを宣言/定義する方法は?

ただし、テンプレートを使用して作業する場合、この方法は壊れます。テンプレートは完全にヘッダファイルに入っている必要があります(コードをcppに入れ、のそれぞれについてtemplate class A<T>;を列挙した場合はカウントされません)。Tがラムダの場合など、常に実現可能なわけではありません。

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()によって提供されていますので、私は「行方不明の定義」エラーを取得すると思います。

おそらく、ヘッダーファイルを整理するためのいくつかのトリックがあります。各ヘッダをクラス定義ファイルとメソッド定義ファイルに分割し、それらをいくつかの素晴らしい方法で含めることによって、または、何かが第3 /第4クラスを介して行われるかもしれません。しかし、私はこれを具体的に行う方法を想像することはできません。

C++ 11/14/17はokです(具体的には、MSVC++ 2017、toolset v141)。

+0

VS15には3つの異なるコンパイラバージョンをインストールすることができるため、コンパイラの年を記載する代わりにバージョンを記載してください(以前のものはすべて1つしかありません)。 – tambre

+0

@ SergeyB。これは、私が与えたコード例の特定の問題を解決します。しかし、私は、 ".hと.cppファイルを持たず、関数定義を含む.inlのような第3の種類のファイルが必要なテンプレートを持つ"の代わりに、どのように管理するかについての一般的なガイドラインが必要です。 ... –

+0

おそらくあなたは、メンバメソッド関数テンプレートを作成し、 '好きstatic_assert'を通じてタイプを強制することができます:'テンプレート<テンプレートクラスC> 無効RUNA(C の* pB { static_assert(STD :: is_same 、B > ::値、 "!"); // ... } '。この方法では、コールポイントで '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ヘッダーは外部コードで使用しないでください.、.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つのファイルにまとめるのは不便です。問題は、これらの宣言/定義をヘッダーファイルに分割し、それらを含める方法です。 –

+1

@ SergeRogatch .hppと.cppと同じ方法です。標準はテンプレート関数定義のために拡張子.ippを使用することです。 –

関連する問題