Variadic templateへのあらゆる操作
※追記
このエントリーにおけるコードはその後改善が施され、それについての新たなエントリが書かれた(See also
)。
variadic templateで遊ぶ - in neuroにインスパイアされて、私も少し遊ぶ事にした。折角なのでリンク先の内容とは異なるヘルパを作る。まず簡単なものから。
Length
Variadic templateの長さを取得する。
template<class Head,class... Tail> constexpr std::size_t Length_v=1+Length_v<Tail...>; template<class Tail> constexpr std::size_t Length_v<Tail> =1; Length_v<int,int,int,int,int,int,int>; // 7
Erase
最初に合致する指定の型を削除する。まず下準備として、かの有名なTypeListをVariadic templateから生成してみる。
using std::experimental::nullopt_t; template<class L,class R> struct TypeList{ using Head=L; using Tail=R; }; template<class L,class... R> struct ToTypeList{ using type=TypeList<L,typename ToTypeList<R...>::type>; }; template<class Tail> struct ToTypeList<Tail>{ using type=nullopt_t; };
これらを用いて、最初に合致した型を削除する。
template<class Target,class... TpL> class Erase{ template<class,class> struct Erasure; template<class Tar,class Head,class Tail> struct Erasure<Tar,TypeList<Head,Tail>>{ using type=TypeList<Head,typename Erasure<Tar,Tail>::type>; }; template<class Tar,class Tail> struct Erasure<Tar,TypeList<Tar,Tail>>{ using type=Tail; }; template<class T> struct Erasure<T,nullopt_t>{ using type=nullopt_t; }; public: using type=typename Erasure<Target,typename ToTypeList<TpL...>::type>::type; }; typedef typename Erase<int,char,char,char,float,int,char,char>::type type; // TypeList<char, TypeList<char, TypeList<char, TypeList<float, TypeList<char, std::experimental::fundamentals_v1::nullopt_t> > > > >
....と思ったのだが、TypeListに入ったままだと扱いにくいので、この場合初めからVariadic templateを持つPackのみを受け付けるようにしてしまった方が良い。連結とPackからの展開も実装してそれらを利用する。
// Transfer template<template<class...>class,class...> struct Transfer; template<template<class...>class Range,class... All> struct Transfer<Range,pack<All...>>{ using type=Range<All...>; }; // Concat template<class...> struct pack{}; template<class,class...> struct Concat; template<class... L,class... R> struct Concat<pack<L...>,pack<R...>>{ using type=pack<L...,R...>; }; template<class...L,class... R> struct Concat<pack<L...>,R...>{ using type=pack<L...,R...>; }; template<class L,class... R> struct Concat<L,pack<R...>>{ using type=pack<L,R...>; }; template<class L,class... R> using Concat_t=typename Concat<L,R...>::type; // Erase template<class,class> struct Erase; template<class Key,class Head,class... Tail> struct Erase<Key,pack<Head,Tail...>>{ using type=Concat_t<Head,typename Erase<Key,pack<Tail...>>::type>; }; template<class Key,class... Tail> struct Erase<Key,pack<Key,Tail...>>{ using type=pack<Tail...>; }; template<class Tail> struct Erase<Tail,pack<>>{ using type=pack<>; }; template<class Key,class Pack> using Erase_t=typename Erase<Key,Pack>::type; using result_type=typename Transfer<std::tuple,Concat_t<Pack<int,int,int,int>,Pack<char,char,char,char>>>::type; // std::tuple<int,int,int,int,char,char,char,char> using result_type2=typename Transfer<std::tuple,Erase_t<char,Pack<int,int,char,int,char>>>::type; // std::tuple<int,int,int,char>
飛ばし読みをしているだけだ。
EraseAll
指定の型と合致する全ての型を削除する。
template<class,class> struct EraseAll; template<class Key,class Head,class... Tail> struct EraseAll<Key,pack<Head,Tail...>>{ using type=Concat_t<Head,typename EraseAll<Key,pack<Tail...>>::type>; }; template<class Key,class... Tail> struct EraseAll<Key,pack<Key,Tail...>>{ using type=typename EraseAll<Key,pack<Tail...>>::type; }; template<class Tail> struct EraseAll<Tail,pack<>>{ using type=pack<>; }; template<class Key,class Pack> using EraseAll_t=typename EraseAll<Key,Pack>::type; using result_type = typename Transfer< std::tuple, EraseAll_t<char,Pack<int,char,char,float,char,double,char,char,char,bool,int,char,char,float,int,char>> >::type; // std::tuple<int, float, double, bool, int, float, int>
No Duplicate
重複を削除する。
template<class,class...> struct NoDuplicate; template<class Head,class... Tail> struct NoDuplicate<pack<Head,Tail...>>{ private: using inner_result=Erase_t<Head,typename NoDuplicate<pack<Tail...>>::type>; public: using type=Concat_t<Head,inner_result>; }; template<> struct NoDuplicate<pack<>>{ using type=pack<>; }; template<class Head,class... Tail> using NoDuplicate_t=typename NoDuplicate<Head,Tail...>::type; using result_type = typename Transfer< std::tuple, NoDuplicate_t<Pack<int,char,char,float,char,double,char,char,char,bool,int,char,char,float,int,char>> >::type; // std::tuple<int, char, float, double, bool>
Indexof
Variadic templateから指定された型を検索、そのインデックス値を返す。見つからなかった場合、不正値として-1を返す。これは、パックを使う必要はない。
template<class,class...> struct Indexof; template<class Key,class Head,class...Tail> struct Indexof<Key,Head,Tail...>{ private: enum{tmp=Indexof<Key,Tail...>::value}; public: enum{value=tmp==-1?tmp:1+tmp}; }; template<class Head,class... Tail> struct Indexof<Head,Head,Tail...>{ enum{value=0}; }; template<class Tail> struct Indexof<Tail>{ enum{value=-1}; }; template<class Key,class... Args> constexpr std::size_t Indexof_v=Indexof<Key,Args...>::value; Indexof_v<int,char,char,char,char> // -1 Indexof_v<int,char,char,char,int> // 3 Indexof_v<int,int,char,char,char> // 0
ざっと書いてこんなものだろうか。よくよく考えればこの主軸となっている概念は2001年頃からある。