模板互递归

Template Mutual Recursion

我想在我的库中有一个模板函数 func 以便用户可以用自己的类型重载它。问题是我的类型系统的形式是

T := A<T>, B<T>, C

因此,如果 template<class T> void func(A<T>); 专门用于 T = B<C>,则它需要 template<class T> void func(B<T>);。相反,如果我们用 T = A<C> 实例化 func(B<T>)B<T> 特化需要 A<T> 特化。

到这里为止的问题可以通过在一些通用头文件中声明模板函数来解决。

我不知道如何使这种类型系统可扩展。我希望用户可以定义她自己的类型 template<class T> class D<T>; 并实现她自己的 template<class T> void func(D<T>);。在这种情况下,我不知道用户如何转发声明她的类型,以便在特化 A<D<C>> 中函数 void func(A<T>); 能够找到 void func(D<T>);.

有没有标准的方法来做到这一点?

编辑问题的最小工作示例:

// 在 A.hpp

namespace ns {
template<class T> struct A { T t; };
template<class T>
void fun (A<T> a) { fun(a.t); }
}

// B.hpp

namespace ns {
template<class T> struct B { T t; };
template<class T>
void fun (B<T> b) { fun(b.t); }

// C.hpp

#include <iostream>
namespace other {
template<class T>
struct C {};
}

namespace ns {
template<class T>
void fun(other::C<T> c) { std::cout << "C" << std::endl; }
}

// main.cpp

#include "A.hpp"
#include "B.hpp"
#include "C.hpp"

namespace ns {
void f () {
    fun(A<B<other::C<int>>>());
}
}


int main () {
    ns::f();
}

此示例无法编译。如果我们将 main.cpp 中的包含重新排序为

,它就会编译
#include "C.hpp"
#include "B.hpp"
#include "A.hpp"

现在,这显然是黑客攻击。使用此设计,用户将无法同时实例化 A<B<C<int>>>B<A<C<int>>>。一个解决方案是在其他模板中转发声明 AB,并将其包含在 A.hppB.hpp 中。当您尝试让库的用户定义她自己的类型时,问题就来了。如果库的用户定义了她自己的类型template<class T> class D;,她就不能前向声明,然后,如果她试图实例化A<D<C<int>>>,编译就会失败。

在此示例中,命名空间 other 表示我无法控制的命名空间,C 表示其他库中预先存在的 class。这可以被认为是一些 boost class 或类似的。 ns 命名空间是我的库定义的命名空间。

如果 fun() 是模板中的静态方法 class 怎么办?

所以你可以部分特化 class?

我的意思是

// 000.h

#ifndef h_000__h
#define h_000__h

namespace ns
 {
   template <typename T>
   struct foo;
 }

#endif

// 001.h

#ifndef h_001__h
#define h_001__h

#include <iostream>

#include "000.h"

namespace ns
 {
   template<class T>
   struct A
    { T t; };

   template <typename T>
   struct foo<A<T>>
    {
      static void fun (A<T> a)
       { std::cout << "A<T> fun" << std::endl; foo<T>::fun(a.t); }
    };
 }

#endif

// 002.h

#ifndef h_002__h
#define h_002__h

#include <iostream>

#include "000.h"

namespace ns
 {
   template <typename T>
   struct B
    { T t; };

   template <typename T>
   struct foo<B<T>>
    {
      static void fun (B<T> a)
       { std::cout << "B<T> fun" << std::endl; foo<T>::fun(a.t); }
    };
 }

#endif

// 003.h

#ifndef h_003__h
#define h_003__h

#include <iostream>

#include "000.h"

namespace other
 {
   template <typename T>
   struct C
    { };
 }

namespace ns
 {
   template <typename T>
   struct foo<other::C<T>>
    {
      static void fun (other::C<T> a)
       { std::cout << "C<T> fun" << std::endl; }
    };
 }

#endif

// main.cpp

#include "001.h"
#include "002.h"
#include "003.h"

namespace ns
 {
   void f ()
    {
      using type = A<B<other::C<int>>>;

      foo<type>::fun(type{});
    }
 }

int main ()
 {
   ns::f(); // print A<T> fun \n B<T> fun \n C<T> fun \n
 }