# C++ Tuple元組實(shí)現(xiàn) Metaprogramming
核心玩法:模板遞歸祝峻、偏特化
? ? template<typename ... Tn>struct Tuple;
? ?
? ? template<typename T0,typename ... Tn>
? ? struct Tuple<T0,Tn...>{
? ? ? ? T0 head;
? ? ? ? Tuple<Tn...>tail;
? ? ? ? constexpr static int size = sizeof...(Tn)+1;
? ? ? ? Tuple():head(0){
? ? ? ? }
? ? ? ?
? ? ? ? Tuple(const T0 &h,const Tuple<Tn...> &t):head(h),tail(t){
? ? ? ? }
? ? ? ?
? ? ? ? Tuple(const Tuple<T0,Tn...> & a):head(a.head),tail(a.tail){
? ? ? ? }
? ? ?
? ? ? ? template<typename _T0,typename ... _Tn,typename =std::enable_if_t<sizeof...(Tn)==sizeof...(_Tn)>>
? ? ? ? Tuple( _T0&& a, _Tn && ...? n):head(std::forward<_T0>(a)),tail(std::forward<_Tn>(n)...){
? ? ? ? }
? ? };
? ?
? ? template<typename T>
? ? struct Tuple<T>{
? ? ? ? constexpr static int size = 1;
? ? ? ? T head;
? ? ? ?
? ? ? ? Tuple():head(0){
? ? ? ? }
? ? ? ? template<typename _T>
? ? ? ? Tuple( _T&& a):head(a){
? ? ? ? }
? ? };
## 1吟策、類型搜索
從Tuple中搜索某個(gè)類型,只要存在一個(gè),則返回true,否則返回false 例如:Tuple<char,short,double> 類型中是否包含 某個(gè)類型(short)扁誓?只要包含返回true,否則返回false
```
template<typename Patten,typename ...T>struct Search;
? ?
template<typename Patten,typename H,typename ...T>
struct Search<Patten,Tuple<H,T...>>: std::conditional_t<std::is_same_v<Patten, H>,std::true_type, Search<Patten, Tuple<T...>>> {
};
template<typename Patten>
struct Search<Patten,Tuple<>>: std::false_type {
};
///Test
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double> a('s',5,6.8);
? ?
? ? bool x0x = Search<short, decltype(a)>::value;/// true
? ? bool x2x = Search<double, decltype(a)>::value;/// true
? ? bool x3x = Search<unsigned short, decltype(a)>::value;/// false
? ? bool x4x = Search<int, decltype(a)>::value;/// false
?
? ? return 0;
}
```
## 2邓梅、類型搜索(并統(tǒng)計(jì)個(gè)數(shù))
或者,我們可以更近一步乾蛤,從Tuple中搜索某個(gè)類型每界,并且統(tǒng)計(jì)出有多少個(gè)該類型?
```
template<typename Patten,typename ...T>struct Count;
? ?
template<typename Patten,typename H,typename ...T>
struct Count<Patten,Tuple<H,T...>>:std::integral_constant<int, std::is_same_v<Patten, H> + Count<Patten, Tuple<T...>>::value > {
};
template<typename Patten>
struct Count<Patten,Tuple<>>: std::integral_constant<int, 0> {
};
///Test
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double,short,int,double,double> a;
? ?
? ? int c0 = Count<char, decltype(a)>::value;///1
? ? int c1 = Count<short, decltype(a)>::value;///2
? ? int c2 = Count<double, decltype(a)>::value;///3
? ? int c3 = Count<bool, decltype(a)>::value;///0
? ? int c4 = Count<unsigned char, decltype(a)>::value;///0
? ? int c5 = Count<int, decltype(a)>::value;///1
? ?
? ? return 0;
}
```
有了上述統(tǒng)計(jì)類型個(gè)數(shù)的方法家卖,判斷Tuple中是否包含某個(gè)類型還可以修改成如下:
原理:該類型個(gè)數(shù)大于0返回true,否則返回false眨层,不過效率相對(duì)低點(diǎn),因?yàn)椴还苁欠癜紩?huì)完全遍歷完整個(gè)類型列表上荡。
```
///判斷是否存在某個(gè)類型趴樱?
template<typename Patten,typename ...T>
using search_t = std::conditional_t< (Count<Patten, T...>::value > 0) , std::true_type, std::false_type>;
///Test
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double,short,double,double> a;
? ?
? ? bool x0x = search_t<short, decltype(a)>::value;/// true
? ? bool x2x = search_t<double, decltype(a)>::value;/// true
? ? bool x3x = search_t<unsigned short, decltype(a)>::value;/// false
? ? bool x4x = search_t<int, decltype(a)>::value;/// false
? ?
? ? return 0;
}
```
## 3、類型搜索(返回首次匹配到該類型時(shí)的位置)
或者,我們可以更更再近一步叁征,從Tuple中搜索某個(gè)類型纳账,并返回第一次匹配到該類型時(shí)的位置(從1開始),如果沒有匹配到返回0
```
template<typename Patten,typename ...T>struct SearchHIdx;
?
template<typename Patten,typename H,typename ...T>
struct SearchHIdx<Patten,Tuple<H,T...>>: std::integral_constant<int,(std::is_same_v<Patten, H> ? sizeof...(T) :? SearchHIdx<Patten, Tuple<T...>>::value )>{
};
template<typename Patten>
struct SearchHIdx<Patten,Tuple<>>: std::integral_constant<int, 0> {
};
template<typename Patten,typename ...T>struct SearchH;
template<typename Patten,typename ...T>
struct SearchH<Patten,Tuple<T...>> :std::integral_constant<int,((SearchHIdx<Patten, Tuple<T...>>::value == 0) ? 0 : (sizeof...(T) - SearchHIdx<Patten, Tuple<T...>>::value))>{
};
///Test
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double,short,double,double> a;///6
? ?
? ? int idx0 = SearchH<char, decltype(a)>::value;///1
? ? int idx1 = SearchH<short, decltype(a)>::value;///2
? ? int idx2 = SearchH<double, decltype(a)>::value;///3
? ? int idx3 = SearchH<int, decltype(a)>::value;///0捺疼, Not found
? ? return 0;
}
```
## 4疏虫、萃取Tuple指定位置的類型
我們可以從Tuple中萃取指定位置的類型,例如第一個(gè)啤呼、最后一個(gè)卧秘、或者第n個(gè)...
可以例如模版函數(shù)聲明,代碼如下:
```
template<typename Head,typename ...Trail>
Head ForntT(Tuple<Head,Trail...>);///獲取第一個(gè)類型
///template<typename ...Head,typename Trail>///獲取最后一個(gè)類型
///Trail BackT(Tuple<Head...,Trail>);///【無法使用該模板】:因?yàn)門uple<Head...,Trail>類型作為函數(shù)參數(shù)編譯器推斷不出來
template<typename Head,typename ...Trail>
Tuple<Trail...> PopForntT(Tuple<Head,Trail...>);///移除第一個(gè)類型
template<typename Head,typename ...Trail>
Tuple<Head,Trail...> PushForntT(Head ,Tuple<Trail...>);///插入類型到Tuple的首部
template<typename ...Head,typename Trail>
Tuple<Head...,Trail> PushBackT(Tuple<Head...>,Trail);///插入類型到Tuple的尾部
///Test
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double> a;
?
? ? decltype(ForntT(a)) x1;/// char x1;
? ? decltype(PopForntT(a)) x2;///? Tuple<short,double> x2;
? ? decltype(PushBackT(a,false)) x3;///? Tuple<char,short,double,bool> x3;
? ?
? ? return 0;
}
```
通過上述函數(shù)模板的聲明媳友,可以利用編譯器推斷出來Tuple的部分位置的情況斯议,但是上述方法無法推斷出Tuple的最后一個(gè)類型,或者移除最后一個(gè)類型醇锚,我們得想其他辦法哼御。我們嘗試用類模板偏特化試試看:
```
template<typename T>struct FrontC;
template<typename Head,typename ...Trail>
struct FrontC<Tuple<Head,Trail...>>{///獲取第一個(gè)類型
? ? using type = Head;
};
//template<typename T>struct BackC;
//
//template<typename ...Head,typename Trail>
//struct BackC<Tuple<Head...,Trail>>{///獲取最后一個(gè)類型,無法編譯通過
//? ? using type = Trail;
//};
template<typename T>struct PopFrontC;
template<typename Head,typename ...Trail>
struct PopFrontC<Tuple<Head,Trail...>>{///移除第一個(gè)類型
? ? using type = Tuple<Trail...>;
};
template<typename Head,typename ...Trail>struct PushFrontC;
template<typename Head,typename ...Trail>
struct PushFrontC<Head,Tuple<Trail...>>{///插入類型到Tuple首部
? ? using type = Tuple<Head,Trail...>;
};
template<typename Trail,typename ...Head>struct PushBackC;
template<typename Trail,typename ...Head>
struct PushBackC<Trail,Tuple<Head...>>{///插入類型到Tuple尾部
? ? using type = Tuple<Head...,Trail>;
};
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double> a;
?
? ? FrontC<decltype(a)>::type x1;/// char x1;
? ? PopFrontC<decltype(a)>::type x2;/// Tuple<short,double> x2;
? ? PushFrontC<bool, decltype(a)>::type x3;/// Tuple<bool,char,short,double> x3;
? ? PushBackC<int, decltype(a)>::type x4;/// Tuple<char,short,double,int> x4;
? ?
? ? return 0;
}
```
發(fā)現(xiàn)類模板也無法萃取出Tuple的最后一個(gè)類型焊唬,或者移除最后一個(gè)類型恋昼。因?yàn)門uple<Head...,Trail>這樣的類型,無法被偏特化赶促。
### 萃取Tuple的指定位置的類型
通過Supscript可以萃取指定位置的類型液肌,代碼如下:
```
template <int idx, typename ...T>
struct Supscript;
template <int idx,typename H, typename ...T>
struct Supscript<idx,Tuple<H,T...>>{
private:
? ? using last = typename Supscript<idx-1,Tuple<T...>>::type;
public:
? ? using type = std::conditional_t<idx==0, H,last>;
};
template <typename H,typename ...T>
struct Supscript<0,Tuple<H,T...>>{
? ? using type = H;
};
///為何不能這樣寫?
///template <typename ...T>
///using last_t = typename Supscript<sizeof...(T)-1, T...>::type;
template<typename ...T>struct LastType;
template<typename ...T>
struct LastType<Tuple<T...>>{
? ? using type = typename Supscript<sizeof...(T)-1, Tuple<T...>>::type;
};
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double,bool> a;
? ? Tuple<short,double> av;
? ? Tuple<char> ab;
? ?
? ? Supscript<0, decltype(a)>::type xx4;
? ? Supscript<1, decltype(a)>::type xx43;
? ? Supscript<2, decltype(a)>::type x4x4;
? ? Supscript<3, decltype(a)>::type xx554;
? ? Supscript<0, decltype(av)>::type xx444;
? ? Supscript<1, decltype(av)>::type xx334;
? ?
? ? LastType<decltype(a)>::type xaa2a;///bool xaa2a;
? ? LastType<decltype(av)>::type xaa3a;///double xaa3a;
? ? LastType<decltype(ab)>::type xa4aa;///char xa4aa;
? ?
? ? return 0;
}
```
上述代碼鸥滨,也可以用另外一種寫法實(shí)現(xiàn)【好像換湯不換藥嗦哆,邏輯是一樣的】,修改后如下:
```
template <int idx, typename ...T>
struct Supscript;
template <int idx,typename H, typename ...T>
struct Supscript<idx,Tuple<H,T...>>{
? ? using type = typename? Supscript<idx-1,Tuple<T...>>::type;
};
template <typename H, typename ...T>
struct Supscript<0,Tuple<H,T...>>{
? ? using type = H;
};
template <int idx, typename ...T>
using supscript_t = typename Supscript<idx,T...>::type;
///test
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double,bool> a;
? ? Tuple<short,double> av;
? ? Tuple<char> ab;
? ?
? ? supscript_t<0, decltype(a)> xxx0;/// char xxx0;
? ? supscript_t<1, decltype(a)> xxx1;/// short xxx1;
? ? supscript_t<2, decltype(a)> xxx2;/// double xxx2;
? ? supscript_t<3, decltype(a)> xxx3;///bool xxx3;
? ? supscript_t<0, decltype(av)> xxx4;///short xxx4
? ? supscript_t<1, decltype(av)> xxx5;///double xxx5;
? ? supscript_t<0, decltype(ab)> xxx6;/// char xxx6;
? ? supscript_t<1, decltype(ab)> xxx7;/// Error
? ?
? ? return 0;
}
```
經(jīng)過上述代碼改進(jìn)后婿滓,Supscript依然可以萃取到任意指定位置的類型老速。
## 5、翻轉(zhuǎn)Tuple的類型列表
借助上面PushBackC這個(gè)方法可以輕松實(shí)現(xiàn)Tuple類型反轉(zhuǎn)凸主。
```
template <int idx, typename ...T>
struct Reverse;
template <int idx,typename H, typename ...T>
struct Reverse<idx,Tuple<H,T...>>{
? ? using type =? typename PushBackC<H,typename Reverse<idx-1,Tuple<T...>>::type>::type;
};
template <typename H, typename ...T>
struct Reverse<0,Tuple<H,T...>>{
? ? using type = Tuple<H>;
};
template <int idx, typename ...T>
using reverse_t = typename Reverse<idx, T...>::type;
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double,bool> a;
? ? Tuple<short,double> av;
? ? Tuple<char> ab;
? ?
? ? reverse_t<0, decltype(a)> xfdas0;/// Tuple<char> xfdas0;
? ? reverse_t<1, decltype(a)> xfdas1;/// Tuple<short,char> xfdas1;
? ? reverse_t<2, decltype(a)> xfdas2;/// Tuple<double,short,char> xfdas2;
? ? reverse_t<3, decltype(a)> xfdas3;/// Tuple<bool,double,short,char> xfdas3;
? ? reverse_t<0, decltype(av)> xfdas4;/// Tuple<short> xfdas4;
? ? reverse_t<1, decltype(av)> xfdas5;/// Tuple<double,short> xfdas5;
? ?
? ? return 0;
}
```
## 6橘券、兩個(gè)Tuple合并
可以想辦法把兩個(gè)Tuple內(nèi)部的類型合并成到一個(gè)Tuple中:
```
template<typename T,typename ...T1> struct Merge;
template<typename T,typename T1,typename ...T2>
struct Merge<T,Tuple<T1,T2...>>{
private:
? ? using Head = decltype(PushBackT(T(),T1()));
? ? using Trail = Tuple<T2...>;
public:
? ? using type = typename Merge<Head,Trail>::type;
};
template<typename T,typename T1>
struct Merge<T,Tuple<T1>>{
public:
? ? using type = decltype(PushBackT(T(),T1()));
};
template<typename T,typename ...T1>
using merge_t = typename Merge<T, T1...>::type;
int main(int argc, const char * argv[]) {
? ? Tuple<char,short,double,bool> a;
? ? Tuple<short,double> av;
? ? Tuple<char> ab;
? ?
? ? merge_t<decltype(ab), decltype(av)> x11;///Tuple<char,short,double> x11;
? ? merge_t<decltype(av), decltype(av)> x12;/// Tuple<short,double,short,double> x12;
? ? merge_t<decltype(av), decltype(ab)> x13;/// Tuple<short,double,char> x13;
? ?
? ? return 0;
}
```