我已經看到了一些代碼(https://stackoverflow.com/a/19209751/877329)使用遞歸「繼承」到mangle模板參數包。這種結構背後的想法是什麼?它是如何工作的?繼承與參數模板包。有什麼竅門?
回答
前言
雖然模板遞歸往往是必要的,沒有什麼是必然需要使用繼承。這就是說,它已成爲相當標準的使用模式:
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
,因爲他們有靜態constexpr
value
成員。這種模式強烈表明遞歸繼承是常見的並且受到鼓勵。
遞歸繼承
假設我要確定一個參數包包含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
已被剝離,以便自己進行測試,並且如果需要將保留類型T
和TAIL...
以供將來處理。 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!");
...
}
但是這是在解釋這個棘手的問題第一次嘗試。
在通用代碼中,您不能創建具有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; }
};
- 1. C++模板專門與繼承
- 2. 模板類參數繼承
- 3. 模板繼承:沒有參數取決於模板參數
- 4. 繼承與模板
- 5. 模板函數參數繼承
- 6. 模板繼承和可變參數
- 7. 有沒有什麼辦法讓所有的模板都繼承母模板
- 8. 類具有可變參數模板成員函數繼承
- 9. 什麼時候使用模板與繼承
- 10. 函數模板和私有繼承
- 11. 具有模板函數的繼承類
- 12. 模板繼承C++
- 13. 模板和繼承
- 14. Django模板繼承
- 15. WPF模板繼承
- 16. djangocms模板繼承
- 17. javascript模板繼承
- 18. 玉模板繼承
- 19. Jinja2模板繼承
- 20. 從具有多個參數的模板類繼承時出錯
- 21. 繼承與類別有什麼區別
- 22. 模式可以基於繼承來專門化模板嗎?
- 23. 具有模板參數的模板函數專門化
- 24. 有沒有什麼竅門禁止C宏被稱爲左值?
- 25. 混合模板/非模板繼承分類和成員繼承
- 26. 多繼承與模板接口
- 27. JavaScript doesen't與Django模板繼承工作
- 28. C++訪問繼承類的成員,其中繼承的類是模板參數
- 29. 從模板基類繼承構造函數而不重複模板參數?
- 30. 有什麼竅門可以追加...... CSS嗎?
你在問這種方法的動機嗎?或者你問它是如何工作的?後者是 – KyleKnoepfel
。我在另一個話題上看到了類似的方法。 – user877329
@ user877329難以用詞來解釋:) – Arunmu