TypeListおよびTic Tac Toe

ついに(!) variadic templatesを試してみたかったのvariadic templatesまだこれがない10番目のスタジオに縛られています。 可変長テンプレートを使用するのがどこで役に立たないかについて長い間考えないために、 Typelistがどのように見えるかを試してみるというアイデアが生まれました。 それが何であるかまだわからない人のために、私は途中で説明しようとします、そして退屈している人のために-すぐにスクロールできますTypelistを使用して一種の三目TypelistTypelistます。
だからTypeList

タイプリスト
 namespace internal { struct Void { }; } // internal template<typename ...Args> struct TypeList { typedef internal::Void Head; typedef internal::Void Tail; }; typedef TypeList<> EmptyTypeList; template<typename H, typename ...T> struct TypeList<H, T...> { typedef H Head; typedef TypeList<T...> Tail; }; 


典型的なTypeListは「Head」と「Tail」であり、これらもタイプのリストです。 使用法:
 typedef TypeList<float, double, long double> floating_point_types; 

以前は、C ++ 11がないと、次のようになりました。
古いタイプリスト
 template <class H, class T> struct typelist { typedef H head; typedef T tail; }; typedef typelist<float, typelist<double, long double> > floating_point_types; 

そして役立つマクロ:
 #define TYPELIST_1(T1) typelist<T1, null_typelist> #define TYPELIST_2(T1, T2) typelist<T1, TYPELIST_1(T2) > #define TYPELIST_3(T1, T2, T3) typelist<T1, TYPELIST_2(T2, T3) > ... #define TYPELIST_50... 


しかし今では、 variadic templatesおかげで、マクロとリスト内の型の数の制限を取り除くことができます。
実際、興味深いのは、型のリストを操作する方法、その操作を定義する方法、および最終的にそれが与えるものです(より詳細な説明に興味があり、 モダンC ++デザインを見たことがない人-読むことをお勧めします-これが2001年であることは重要ではありません! )
ご覧のとおり、補助型internal::Voidを定義しました。これはシグナルフラグとして機能し、型のリストは空であると言います(少なくともユーザーが何も指定しなかった場合はTypeList<> 、またはリストからすべてのアイテムが削除されました)。 最初から始めましょう:

Isempty


Isempty
 template<typename TL> struct IsEmpty : std::true_type { }; template<> struct IsEmpty<TypeList<internal::Void, internal::Void>> : std::true_type { }; template<typename ...Args> struct IsEmpty<TypeList<Args...>> : std::integral_constant<bool, std::is_same<typename TypeList<Args...>::Head, internal::Void>::value && IsEmpty<typename TypeList<Args...>::Tail>::value> { }; 


ここでは、他の操作を定義するために必要なほとんどすべてを見ることができます。 ご覧のとおり、まず「バックボーン」を定義しますIsEmpty型は1つの型によってパラメーター化されます。 本質的に、これは1つの引数を取る「関数」です。 タイプTLは「任意のタイプ」を意味するため、空のリストを持つケースのテンプレートの完全な特殊化を行います: TypeList<internal::Void, internal::Void>TypeList<>のみ、またはそのためだけにI定義されたタイプEmptyTypeList )および機能する部分的な特殊化-「任意のタイプリスト用」。 したがって、「関数」はタイプのリストに対してのみ定義されます。 struct IsEmpty : std::true_typeような便利なものが新しい標準に登場しました。これにより、生活が大幅に簡素化されます: struct IsEmpty : std::true_type場合、 IsEmptyvalueクラスのメンバー、多数のtypedefおよびboolへの変換演算子があります。
使い方は?:
 typedef TypeList<int> TL1; std::cout << std::boolalpha << IsEmpty<TL1>::value << " " << IsEmpty<EmptyTypeList>() << std::endl; 

空のリストがあるかどうかは、次の式を定義します。
 std::is_same<typename TypeList<Args...>::Head, internal::Void>::value && IsEmpty<typename TypeList<Args...>::Tail>::value 

文字通り-「リストの先頭がvoidを示す補助型であり、末尾も空のリストである場合、リストは空です。」 ご覧のとおり、ここでは再帰が使用され、空のリストのテンプレートの完全な特殊化が停止されます。
次:

コンテナ


コンテナ
 template<typename T, typename TL> struct Contains : std::false_type { }; template<typename ...Args> struct Contains<internal::Void, Args...> : std::false_type { }; template<typename T, typename ...Args> struct Contains<T, TypeList<Args...>> : std::integral_constant<bool, std::is_same<typename TypeList<Args...>::Head, T>::value || Contains<T, typename TypeList<Args...>::Tail>::value > { }; 


指定したタイプT TLタイプのリスト内にあるかどうかがContains 。 使用法:

使用法:
 typedef TypeList<double, float, float, double, int, char, char, int, char> TL; std::cout << std::boolalpha << Contains<char, TL>::value << " " << Contains<float, TypeList<double>>() << std::endl; 

再び:「リストの先頭がタイプTである場合、 Tはリスト内にあります。それ以外の場合、 Tリストの末尾にあるかどうかを確認します。」
部分的な専門化-予防措置-誰かがタイプinternal::Void使用するとどうなりますか?

長さ


長さ
 template<typename TL> struct Length : std::integral_constant<unsigned int, 0> { }; template<typename ...Args> struct Length<TypeList<Args...>> : std::integral_constant<unsigned int, IsEmpty<TypeList<Args...>>::value ? 0 : 1 + Length<typename TypeList<Args...>::Tail>::value> { }; 


リストが空の場合、長さはゼロです。それ以外の場合は1です(「ヘッド」があるため)+テールの長さ:
 typedef TypeList<double, float, float, double, int, char, char, int, char> TL; std::cout << Length<TL>::value << " " << Length<EmptyTypeList>() << std::endl; 

入力する


 template<unsigned int N, typename TL> struct TypeAt { typedef internal::Void type; }; 

-配列のように、インデックスでタイプを返します。 実装は最初の呼び出しです(タイプNint変更します):
 //template<int N, typename ...Args> //struct TypeAt<N, TypeList<Args...>> //{ // typedef typename std::conditional<N == 0, // typename TypeList<Args...>::Head, // typename TypeAt<N - 1, typename TypeList<Args...>::Tail>::type>::type type; //}; 

-すべて正常に動作しますが、! -インデックスが大きすぎる場合、警告が表示されます。 現在の実装で抜け出すことは可能ですが、ここでは、 N=-1場合にテンプレートを正しくインスタンス化する必要があることを考慮する必要があります。 したがって、我々は他の方法で行きます:
 template<typename ...Args> struct TypeAt<0, TypeList<Args...>> { typedef typename TypeList<Args...>::Head type; }; template<unsigned int N, typename ...Args> struct TypeAt<N, TypeList<Args...>> { static_assert(N < Length<TypeList<Args...>>::value, "N is too big"); typedef typename TypeAt<N - 1, typename TypeList<Args...>::Tail>::type type; }; 

-頭のインデックスはゼロであり、他の場合-インデックスを1つ減らし、尾の一部を「食べる」(左から右に移動します)-インデックスをゼロにし、現在の頭が必要なタイプです! 使用法:
 typedef TypeList<char, short> TL2; static_assert(std::is_same<TypeAt<1, TL2>::type, short>::value, "Something wrong!"); 

リスト出力


演算子<<
 //   std::ostream& operator<<(std::ostream& ostr, EmptyTypeList) { ostr << "{}"; return ostr; } template<typename TL> void PrintTypeListHelper(TL, std::ostream& ostr) { } template<typename T> void PrintTypeListHead(T, std::ostream& ostr) { ostr << typeid(T).name(); } template<typename ...Args> void PrintTypeListHead(TypeList<Args...> tl, std::ostream& ostr) { ostr << tl; } template<typename Head, typename ...Args> void PrintTypeListHelper(TypeList<Head, Args...>, std::ostream& ostr) { PrintTypeListHead(Head(), ostr); if(!IsEmpty<TypeList<Args...>>::value) { ostr << ' '; PrintTypeListHelper<Args...>(TypeList<Args...>(), ostr); } } template<typename ...Args> std::ostream& operator<<(std::ostream& ostr, TypeList<Args...> tl) { ostr << '{'; PrintTypeListHelper(tl, ostr); ostr << '}'; return ostr; } 


これらの関数は、通常のタイプリストとネストされたリストを正確に表示するのに役立ちます。たとえば、
 typedef TypeList<double, float, float, double, int, char, char, int, char> TL; std::cout << TL() << std::endl; typedef TypeList<TL2, double, TL2> TL10; std::cout << TL10() << std::endl; 

{double float float double int char char int char}

{{char short} double {char short}}


追加して追加


追加、追加
リストの末尾に追加する関数。わずかな違いがあります。
 template<typename TOrTL2, typename TL> struct Append { }; template<typename T, typename ...Args> struct Append<T, TypeList<Args...>> { typedef TypeList<Args..., T> type; }; template<typename ...Args1, typename ...Args2> struct Append<TypeList<Args1...>, TypeList<Args2...>> { typedef TypeList<Args2..., Args1...> type; }; template<typename T, typename TL> struct Add { }; template<typename T, typename ...Args> struct Add<T, TypeList<Args...>> { typedef TypeList<Args..., T> type; }; 


最初の引数に型のリストを指定してAppendを使用すると、コンポーネントへの「分解」があります。 すなわち:
 typedef TypeList<int> TL1; typedef TypeList<char, short> TL2; std::cout << TL1() << ", " << TL2() << std::endl; std::cout << Add<TL2, TL1>::type() << ", " << Append<TL2, TL1>::type() << std::endl; 

{int}, {char short}
{int {char short}}, {int char short}
最初の場合、結果の長さは2で、2番目の場合は3になります。これは、追加されたタイプのリストがコンポーネントに「分解」されるためです。

すべて削除


アイテムを削除する
 template<typename TOrTL2, typename TL> struct RemoveAll { }; template<typename T, typename ...Args> struct RemoveAll<T, TypeList<Args...>> { private: typedef typename RemoveAll<T, typename TypeList<Args...>::Tail>::type Removed; typedef typename TypeList<Args...>::Head Head; public: typedef typename std::conditional< std::is_same<Head, T>::value, Removed, typename Append<Removed, TypeList<Head>>::type >::type type; }; template<typename T, typename Head> struct RemoveAll<T, TypeList<Head>> { typedef typename std::conditional< std::is_same<Head, T>::value, EmptyTypeList, TypeList<Head>>::type type; }; template<typename T> struct RemoveAll<T, EmptyTypeList> { typedef EmptyTypeList type; }; 


削除は次のように行われます。

重要なことは、結果を末尾から削除するとき、結果を別のタイプのリストにグループ化し、結合するときに、グループ化されたタイプのリストを「巻き戻す」 Appendを使用することです。
使用法:
 typedef TypeList<double, float, float, double, int, char, char, int, char> TL; std::cout << TL() << std::endl; std::cout << RemoveAll<char, TL>::type() << std::endl; 

{double float float double int char char int char}
{double float float double int int}

RemoveAll別のバージョンを記述して、2番目のタイプのリストから、最初のタイプにあるすべてのタイプを削除できます。 しかし! この場合、他のリストを含むリストには使用できません。

RemoveAll v2
 //template<typename Head2, typename ...Args1> //struct RemoveAll<TypeList<Head2>, TypeList<Args1...>> //{ // typedef typename RemoveAll<Head2, TypeList<Args1...>>::type type; //}; // //template<typename ...Args1> //struct RemoveAll<EmptyTypeList, TypeList<Args1...>> //{ // typedef TypeList<Args1...> type; //}; // //template<typename ...Args2, typename ...Args1> //struct RemoveAll<TypeList<Args2...>, TypeList<Args1...>> //{ //private: // typedef TypeList<Args2...> TL2; // typedef TypeList<Args1...> TL1; // // typedef typename RemoveAll<typename TL2::Tail, TL1>::type Removed; // typedef typename TL2::Head Head2; // //public: // typedef typename std::conditional< // Contains<Head2, Removed>::value, // typename RemoveAll<Head2, Removed>::type, // TL1 // >::type type; //}; 


例:
 typedef TypeList<double, float, float, double, int, char, char, int, char> TL; typedef TypeList<char, double> TL2; std::cout << TL() << std::endl; std::cout << RemoveAll<TL2, TL>::type() << std::endl; 

{double float float double int char char int char}
{float float int int}

RemoveDuplicates


RemoveDuplicates
 template<typename TL> struct RemoveDuplicates { }; template<> struct RemoveDuplicates<EmptyTypeList> { typedef EmptyTypeList type; }; template<typename ...Args> struct RemoveDuplicates<TypeList<Args...>> { private: typedef TypeList<Args...> TL; typedef typename RemoveAll<typename TL::Head, typename TL::Tail>::type HeadRemovedFromTail; typedef typename RemoveDuplicates<HeadRemovedFromTail>::type TailWithoutDuplicates; public: typedef typename Append<TailWithoutDuplicates, TypeList<typename TL::Head>>::type type; }; 


重複を削除する関数:

例:
 typedef TypeList<double, float, float, double, int, char, char, int, char> TL; std::cout << TL() << std::endl; std::cout << RemoveDuplicates<TL>::type() << std::endl; 

{double float float double int char char int char}
{double float int char}

見つける


リスト内の位置を入力
 struct Constants { typedef std::integral_constant<unsigned int, UINT_MAX> npos; }; namespace internal { template<typename T, unsigned int IndexFrom, typename TL> struct FindHelper : std::integral_constant<unsigned int, 0> { }; template<typename T, unsigned int IndexFrom> struct FindHelper<T, IndexFrom, EmptyTypeList> : std::integral_constant<unsigned int, 0> { }; template<typename T, unsigned int IndexFrom, typename ...Args> struct FindHelper<T, IndexFrom, TypeList<Args...>> : std::integral_constant<unsigned int, std::is_same<typename TypeList<Args...>::Head, T>::value ? IndexFrom : IndexFrom + 1 + FindHelper<T, IndexFrom, typename TypeList<Args...>::Tail>::value> { }; } // internal template<typename T, typename TL> struct Find { }; template<typename T> struct Find<T, EmptyTypeList> : Constants::npos { }; template<typename ...Args> struct Find<internal::Void, TypeList<Args...>> : Constants::npos { }; template<typename T, typename ...Args> struct Find<T, TypeList<Args...>> : std::integral_constant<unsigned int, Contains<T, TypeList<Args...>>::value ? internal::FindHelper<T, 0, TypeList<Args...>>::value : Constants::npos::value> { }; 



いくつかのこと:
- Constants -定数用。 私たちの場合、要素が見つからなかったことを示す定数に対してのみ(constexpは私のスタジオではサポートされていないため、 UINT_MAX
- internal::FindHelper実際、リスト内の型を正確に検索する「もの」(!)この型は(追加のIndexFromパラメーター-参照の初期値であり、必要なものではありません:)-必要な場合のために設計されています検索を開始する位置を尋ねます)

再び-複雑なことはありません-指定された型とリストの先頭の型が同じ場合-インデックスはゼロです。そうでない場合は、1tsuだけ右に移動し、リストの末尾に対して同じことを行います。
例:
 typedef TypeList<double, float, float, double, int, char, char, int, char> TL; std::cout << std::boolalpha << std::is_same<TypeAt<Find<double, TL>::value, TL>::type, double>() << std::endl; 


スライス


スライス
 namespace internal { template<unsigned int IndexBegin, unsigned int IndexEnd, typename TL> struct SliceHelper { }; template<unsigned int IndexBegin, unsigned int IndexEnd> struct SliceHelper<IndexBegin, IndexEnd, EmptyTypeList> { typedef EmptyTypeList type; }; template<unsigned int IndexBegin, typename ...Args> struct SliceHelper<IndexBegin, IndexBegin, TypeList<Args...>> { typedef TypeList<typename TypeAt<IndexBegin, TypeList<Args...>>::type> type; }; template<unsigned int IndexBegin, unsigned int IndexEnd, typename ...Args> struct SliceHelper<IndexBegin, IndexEnd, TypeList<Args...>> { private: static_assert(IndexEnd >= IndexBegin, "Invalid range"); typedef TypeList<Args...> TL; public: typedef typename Add< typename TypeAt<IndexEnd, TL>::type, typename SliceHelper<IndexBegin, IndexEnd - 1, TL>::type >::type type; }; } // internal template<unsigned int IndexBegin, unsigned int IndexAfterEnd, typename TL> struct Slice { }; template<unsigned int IndexBegin, unsigned int IndexEnd, typename ...Args> struct Slice<IndexBegin, IndexEnd, TypeList<Args...>> { typedef typename internal::SliceHelper<IndexBegin, IndexEnd, TypeList<Args...>>::type type; }; template<unsigned int Index, typename TL> struct CutTo { }; template<unsigned int Index, typename ...Args> struct CutTo<Index, TypeList<Args...>> { typedef typename Slice<0, Index, TypeList<Args...>>::type type; }; template<unsigned int Index, typename TL> struct CutFrom { }; template<unsigned int Index, typename ...Args> struct CutFrom<Index, TypeList<Args...>> { private: typedef TypeList<Args...> TL; public: typedef typename Slice<Index, Length<TL>::value - 1, TL>::type type; }; 


リストの指定された部分を「切り取る」:

Source: https://habr.com/ru/post/J220217/


All Articles