2017-07-30 74 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。 –