type_traitsテクノロジーとC++


引用する
1つの方法の実現過程では、ビジネスロジックの多くは似ているが、具体的な特化タイプとは異なる.このとき,特化テンプレートを用いて実現することができ,異なるタイプは異なる特化実装を用いる.しかし、このような状況は一定のビジネスロジックの冗長性をもたらします.一方、traitテクノロジーは、特化タイプをカプセル化することによって、同じビジネスロジックを統一的な呼び出しで実現することができる.
Type_traitsテクノロジー
type_traitsはタイプ抽出器またはタイプ抽出器に翻訳することができ、率直に言えば、このメカニズムによって操作されるデータ型のいくつかの特徴を取得することができる.このメカニズムはテンプレートコードを記述する際に特に有用であり,コンパイル中にデータ型の特徴に基づいて異なるコードに割り当てて処理することができる.
STLでのcopyに関するコード
// This header file provides a framework for allowing compile time dispatch
// based on type attributes. This is useful when writing template code.
// For example, when making a copy of an array of an unknown type, it helps
// to know if the type has a trivial copy constructor or not, to help decide
// if a memcpy can be used.
struct __true_type {
};
struct __false_type {
};
template <class _Tp>
struct __type_traits { 
   typedef __true_type     this_dummy_member_must_be_first;
                   /* Do not remove this member. It informs a compiler which
                      automatically specializes __type_traits that this
                      __type_traits template is special. It just makes sure that
                      things work if an implementation is using a template
                      called __type_traits for something unrelated. */
   /* The following restrictions should be observed for the sake of
      compilers which automatically produce type specific specializations 
      of this class:
          - You may reorder the members below if you wish
          - You may remove any of the members below if you wish
          - You must not rename members without making the corresponding
            name change in the compiler
          - Members you add will be treated like regular members unless
            you add the appropriate support in the compiler. */
 
   typedef __false_type    has_trivial_default_constructor;
   typedef __false_type    has_trivial_copy_constructor;
   typedef __false_type    has_trivial_assignment_operator;
   typedef __false_type    has_trivial_destructor;
   typedef __false_type    is_POD_type;
};
// The class template __type_traits provides a series of typedefs each of
// which is either __true_type or __false_type. The argument to
// __type_traits can be any type. The typedefs within this template will
// attain their correct values by one of these means:
//     1. The general instantiation contain conservative values which work
//        for all types.
//     2. Specializations may be declared to make distinctions between types.
//     3. Some compilers (such as the Silicon Graphics N32 and N64 compilers)
//        will automatically provide the appropriate specializations for all
//        types.
// EXAMPLE:
//Copy an array of elements which have non-trivial copy constructors
template <class T> void copy(T* source, T* destination, int n, __false_type);
//Copy an array of elements which have trivial copy constructors. Use memcpy.
template <class T> void copy(T* source, T* destination, int n, __true_type);
//Copy an array of any type by using the most efficient copy mechanism
template <class T> inline void copy(T* source,T* destination,int n) {
   copy(source, destination, n,
        typename __type_traits<T>::has_trivial_copy_constructor());
}

PODとは、Plain Old Data、すなわち、性別または従来のC struct型別を意味する.POD性別は必ずtrivial ctor/dctor/copy/assignment関数を持つので、POD型に対して最も有効な複製方法を採用し、non-POD型に対して最も安全な方法を採用することができる.
// uninitialized_copy
// Valid if copy construction is equivalent to assignment, and if the
//  destructor is trivial.
template <class _InputIter, class _ForwardIter>
inline _ForwardIter 
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
                         _ForwardIter __result,
                         __true_type)
{
  return copy(__first, __last, __result);
}
template <class _InputIter, class _ForwardIter>
_ForwardIter 
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
                         _ForwardIter __result,
                         __false_type)
{
  _ForwardIter __cur = __result;
  __STL_TRY {
    for ( ; __first != __last; ++__first, ++__cur)
      _Construct(&*__cur, *__first);
    return __cur;
  }
  __STL_UNWIND(_Destroy(__result, __cur));
}
template <class _InputIter, class _ForwardIter, class _Tp>
inline _ForwardIter
__uninitialized_copy(_InputIter __first, _InputIter __last, _ForwardIter __result, _Tp*)
{
  typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;
  return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());
}

trait技術とtemplateメタプログラミングの例
template<template<int> class LOGICAL, class SEQUENCE>
struct sequence_any;

template<template<int> class LOGICAL, int NUM, int...NUMS>
struct sequence_any<LOGICAL, sequence<NUM, NUMS...> >
{
	static const bool value = LOGICAL<NUM>::value || sequence_any<LOGICAL, sequence<NUMS...>>::value;
};

template<template<int> class LOGICAL>
struct sequence_any<LOGICAL, sequence<> >
{
	static const bool value = false;
};
template<int A>
struct static_is_zero
{
	static const bool value = false;
};
template<>
struct static_is_zero<0>
{
	static const bool value = true;
};
 const bool SINGLEROWOPT = 
sequence_any<static_is_zero, sequence<SPECIALIZATIONS...>>::value;

参考学習可能なC++コード
  • Trait技術実装反復器
  • https://github.com/cwlseu/codefeeling

  • その他の関連問題
    関数の呼び出しプロセス
    1つのプログラムに同じ名前の関数が複数ある場合、コンパイラはどの関数を呼び出すべきかをどのように探しますか?コンパイラは次の順序で検索されます.
  • 関数直接マッチング
  • テンプレート関数
  • 一定のステルス変換データ型で呼び出せる
  • #include 
    void func(float a) {
      std::cout << "float func:" << a << std::endl;
    }
    void func(int a) {
      std::cout << "int func:" << a << std::endl;
    }
    template <class T>
    void func(T a) {
      std::cout << "template func:" << a << std::endl;
    }
    int main(int argc, char const *argv[])
    {
      int ia = 1;
      func(ia);
      func<int>(ia);
      float fb = 2;
      func(fb);
      func<float>(fb);
      double db = 3;
      func(db);
      func<double>(db);
      return 0;
    }
    

    結果出力
    int func:1 template func:1 float func:2 template func:2 template func:3 template func:3
    テンプレート関数の宣言と定義には一般的に2つの方法があります.
  • 宣言はヘッダファイルに定義される.この場合、テンプレートは異なるタイプの処理方法に対して同じであることが多く、ヘッダファイルに直接配置することができます.実際の呼び出し中にtemplateの呼び出しを実現する場合
  • 宣言+ヘッダファイルに特化し、実際にcppファイルに定義する.このような状況は往々にしていくつかのものを特化している.

  • テンプレートinvokeテンプレート関数
    2つのテンプレート関数は、呼び出されたテンプレート関数がヘッダファイルに宣言されている場合にのみ定義され、特化する.テンプレートの実際の定義はcppファイルでundefinedの問題が発生する.
    これは、ヘッダファイルにおいてテンプレート関数を呼び出す過程で、特化する呼び出された関数が見つからないためである.ヘッダファイルには、特化宣言が呼び出す関数が表示され、この場合、異なるタイプの特化に対して異なる処理スキームがあることが好適である.あるいは、テンプレート関数の定義を直接ヘッダファイルに格納、このような比較的すべての関数に適用する場合に適している.