C++ Tuple元组实现 Metaprogramming

# C++ Tuple元组实现 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中搜索某个类型,只要存在一个,则返回true,否则返回false 例如:Tuple<char,short,double> 类型中是否包含 某个类型(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、类型搜索(并统计个数)

或者,我们可以更近一步,从Tuple中搜索某个类型,并且统计出有多少个该类型?

```

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;

}

```

有了上述统计类型个数的方法,判断Tuple中是否包含某个类型还可以修改成如下:

原理:该类型个数大于0返回true,否则返回false,不过效率相对低点,因为不管是否包含都会完全遍历完整个类型列表。

```

///判断是否存在某个类型?

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、类型搜索(返回首次匹配到该类型时的位置)

或者,我们可以更更再近一步,从Tuple中搜索某个类型,并返回第一次匹配到该类型时的位置(从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中萃取指定位置的类型,例如第一个、最后一个、或者第n个...

可以例如模版函数声明,代码如下:

```

template<typename Head,typename ...Trail>

Head ForntT(Tuple<Head,Trail...>);///获取第一个类型

///template<typename ...Head,typename Trail>///获取最后一个类型

///Trail BackT(Tuple<Head...,Trail>);///【无法使用该模板】:因为Tuple<Head...,Trail>类型作为函数参数编译器推断不出来

template<typename Head,typename ...Trail>

Tuple<Trail...> PopForntT(Tuple<Head,Trail...>);///移除第一个类型

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;

}

```

通过上述函数模板的声明,可以利用编译器推断出来Tuple的部分位置的情况,但是上述方法无法推断出Tuple的最后一个类型,或者移除最后一个类型,我们得想其他办法。我们尝试用类模板偏特化试试看:

```

template<typename T>struct FrontC;

template<typename Head,typename ...Trail>

struct FrontC<Tuple<Head,Trail...>>{///获取第一个类型

    using type = Head;

};

//template<typename T>struct BackC;

//

//template<typename ...Head,typename Trail>

//struct BackC<Tuple<Head...,Trail>>{///获取最后一个类型,无法编译通过

//    using type = Trail;

//};

template<typename T>struct PopFrontC;

template<typename Head,typename ...Trail>

struct PopFrontC<Tuple<Head,Trail...>>{///移除第一个类型

    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;

}

```

发现类模板也无法萃取出Tuple的最后一个类型,或者移除最后一个类型。因为Tuple<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;

}

```

上述代码,也可以用另外一种写法实现【好像换汤不换药,逻辑是一样的】,修改后如下:

```

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;

}

```

经过上述代码改进后,Supscript依然可以萃取到任意指定位置的类型。

## 5、翻转Tuple的类型列表

借助上面PushBackC这个方法可以轻松实现Tuple类型反转。

```

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、两个Tuple合并

可以想办法把两个Tuple内部的类型合并成到一个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;

}

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容