type_traits技術與C++

引言

一個方法實現過程中,業務邏輯很多都是相似的,但是與具體的特化類型的不同有一定的差異。
這個時候可以採用特化模板的方式實現,不同的類型使用不同的特化實現。但是這種情況造成一定的業務邏輯的冗餘。而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++代碼

其他相關問題

函數的調用過程

如果一個程序中很多多個同名的函數,那編譯器是如何找應該調用哪一個函數呢?
編譯器會通過如下順序進行查找。

  1. 函數直接匹配
  2. 模板函數
  3. 通過一定的隱形轉換數據類型可以調用
#include <iostream>
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

模板函數的聲明與定義一般有兩種方式

  1. 聲明定義在header文件中。這種情況往往是模板針對不同的類型處理方式是一樣的,這樣可以直接放到頭文件中。當實際調用過程中實現template的調用
  2. 聲明+特化在頭文件中,實際定義在cpp文件中。這種情況往往特化幾種就是幾種。

模板invoke模板函數

兩個模板函數, 如果被調用的模板函數的只有聲明在頭文件中,定義與特化. 而模板的實際定義在cpp文件中,就會出現undefined的問題.

這是由於在頭文件中進行調用模板函數過程中,找不到特化的被調用函數.
在頭文件中顯示特化聲明被調用的函數, 這種情況比較適合針對不同的類型的特化有不同的處理方案.
或者直接將模板函數定義放到頭文件中,這種比較適合所有的函數都適用一種情況.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章