2016-06-13 63 views

回答

1

前言

虽然模板递归往往是必要的,没有什么是必然需要使用继承。这就是说,它已成为相当标准的使用模式:

template <typename T, typename = void> 
struct has_trait : std::false_type {}; 

template <typename T> 
struct has_trait<T, std::enable_if_t<some_predicate>> : std::true_type {}; 

也就是说,该标准还提供了方便的类型std::{true,false}_type,因为他们有静态constexprvalue成员。这种模式强烈表明递归继承是常见的并且受到鼓励。

递归继承

假设我要确定一个参数包包含T类型的参数。要做到这一点,我要创建类型的实现struct

template <bool B, typename T, typename ... ARGS> 
struct pack_with_type_impl; 

非类型参数B是“匹配”参数; T是我们感兴趣匹配的参数; ARGS...是参数包。

我们将使用此模板的特化来处理各种情况。

空参数包

让其中的参数组是空的接这个案子。在这种情况下,专业化的样子:

template <bool B, typename T> 
struct pack_with_type_impl<B,T> : std::false_type {}; 

有明显的空参数包没有类型T,使专业化的std::false_type继承。

匹配参数包

现在让我们说,它已确定类型T的参数不在参数包存在。在这种情况下,专业化的样子:

template <typename T, typename ... ARGS> 
struct pack_with_type_impl<true, T, ARGS...> : std::true_type {}; 

的递推

现在是最有趣的部分。到目前为止,我们没有进行递归,因为上面的例子代表了终止条件:一个参数包是空的,所以没有什么可做的了。还有一场比赛已经找到了,所以没有什么可做的了。但让我们现在做的部分,其中还没有找到了一个匹配项。在这种情况下,我们会有这样的事情:

template <typename T, typename H, typename ... TAIL> 
struct pack_with_type_impl<false, T, H, TAIL...> : 
    pack_with_type_impl<std::is_same<T,H>::value, T, TAIL...> {}; 

什么?在这种情况下,匹配是false,所以它将从模板继承,只提供一个参数。也就是说,对应于参数包的头部的参数H已被剥离,以便自己进行测试,并且如果需要将保留类型TTAIL...以供将来处理。 std::is_same功能只是检查类型是否相同。继承链继续前进,每次剥离头部,直到达到终止条件之一。

举个例子,假设我想检查一个参数包是否有类型int,我提供的参数是:char,double,float。继承链是这样的:

pack_with_type_impl<false,int,char,double,float> 
    ==> pack_with_type_impl<false,int,double,float> 
    ==> pack_with_type_impl<false,int,float> 
     ==> pack_with_type_impl<false,int> # and since the parameter pack is empty now... 
     ==> std::false_type 

在另一方面,如果我提供的参数char,int,double,继承链将是:

pack_with_type_impl<false,int,char,int,float> 
    ==> pack_with_type_impl<false,int,int,float> 
    ==> pack_with_type_impl<true,int,float> # and since the first argument is true 
     ==> std::true_type 

评论

有当然更优雅的方式来做到这一点。对于初学者来说,我可能会创建沿线的几个别名模板:

template <typename ... ARGS> 
using has_int = pack_with_type_impl<false,int,ARGS...>; 

,这样我可以打电话:

template <typename ... ARGS> 
void increment_int(std::tuple<ARGS...>& tup) { 
    static_assert(has_int<ARGS...>::value, 
       "Can only call increment_int on tuples with an int type!"); 
    ... 
} 

但是这是在解释这个棘手的问题第一次尝试。

1

在通用代码中,您不能创建具有N个直接成员的类。然而,您可以创建一个带有直接成员和N-1继承成员的类,专门为N==1结束递归。

示例:假设您要创建一个类,使Foo<int, double, std::string>包含3个成员函数void Foo::f(int); void Foo::f(int); void Foo::f(std::string)。这不是直接可能的,因为N = 3。但是,您可以从Foo<double, std::string>派生并添加一个成员void f(int)

template<typename HEAD, typename... Tail> 
class Foo : public Foo<Tail...> 
{ 
    public: void f(HEAD h) { std::cout << h; } 
}; 

template<typename HEAD> 
class Foo<HEAD> 
{ 
    public: void f(HEAD h) { std::cout << h; } 
};