2016-06-13 62 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; } 
};