STL運用のC++技術(2)——テンプレート特化


STLはC++標準ライブラリの重要な構成部分の一つであり、多重化可能なコンポーネントライブラリだけでなく、アルゴリズムとデータ構造を含むソフトウェアフレームワークであり、C++汎用プログラミングの良い例でもある.STLには多くのC++の高度な技術が用いられている.テンプレート特化技術の運用を紹介する.主に『C++Primer』と『STLソース剖析』を参考にした.
STLにはモジュールが多く用いられており,テンプレートはクラスや関数を作成する式といえる.しかし、インスタンス化される可能性のあるすべてのタイプに最適なテンプレートを書くことはできません.関数テンプレートの特化例を挙げます.
template <typename T>
int Compare(const T &x, const T &y)
{
	if(x < y)
		return -1;
	else if(x > y)
		return 1;
	else
		return 0;
}

上記の関数テンプレートでは、2つの文字列ポインタで呼び出されると、ポインタ値、すなわち文字列のサイズではなくアドレスサイズが比較されます.したがって、Compare関数を文字列に使用できるようにするには、Cスタイルの文字列をどのように比較するかを知っている特別な定義が必要です.これがテンプレートの特化です.
テンプレート特化(template specialization)の定義は、1つ以上のテンプレートパラメータを指定する実際のタイプまたは実際の値です.たとえば、Compareテンプレート関数の特化バージョンを定義できます.
template <> //template         
int Compare(const char * const &x, const char * const &y) //              
{
	return strcmp(x, y);
}
    
以上簡単にテンプレート特化を述べたが,ここではテンプレート特化のSTLでの運用を紹介し,反復器での運用を例に挙げる.
反復器はSTLの鍵であり,もともと分離されたデータ容器とアルゴリズムをよく接着している.たとえば、次のSTLの関数(ソースコードから抜粋)は、名前を変更しながらコードを省略しますが、問題を説明するのに十分です.この関数は反復器によって容器のデータを交換し,反復器はデータ容器とアルゴリズムの橋渡しであり,アルゴリズムは容器の具体的な構造に関心を持たずにデータ容器の反復器によって容器中のデータにアクセスする.
//       ,    
template <class Iter1, class Iter2, class T>
inline void _iter_swap(Iter1 a, Iter2 b, T) {
  T tmp = *a;
  *a = *b;
  *b = tmp;
}
//            ,    
template <class Iter1, class Iter2>
inline void iter_swap(Iter1 a, Iter2 b) {
 _iter_swap(a, b, VALUE_TYPE(Iter1)); //VALUE_TYPE         
}
    
VALUEを使用しましたTYPE呼び出しは、反復器を返す値のタイプを注記しています.具体的には、後述します.この例を挙げると,この呼び出しを引き出すためである.本文はテンプレートの特化を話していますが、ここまで来たらもう問題を逃したようで、何が起こっているのか分かりません.敷物の差が少なくなったので,本題に入る.
質問ですiterswapという関数のパラメータは反復器であり、関数の内部に一時変数を定義する必要があります.変数のデータ型は反復器が指すデータ型です.では、反復器が指すデータ型をどのように知っていますか?テンプレートの実参推定メカニズムを利用して,この問題を解決できるという人もいる.コードは次のとおりです.
//       
template <class Iter1, class Iter2, class T>
inline void _iter_swap(Iter1 a, Iter2 b, T) {
  T tmp = *a;
  *a = *b;
  *b = tmp;
}
//            
template <class Iter1, class Iter2>
inline void iter_swap(Iter1 a, Iter2 b) {
 _iter_swap(a, b, *a); //      
}
      
しかし,関数の戻りタイプを導くと,テンプレート実パラメトリック推定機構は失効する.テンプレート実参推定メカニズムの具体的な内容は,本シリーズ(3)で紹介する.以上の質問に続き、VALUEという名前を使用しました.TYPEの呼び出しは、反復器が指すデータ型を抽出する抽出剤のようなものである.どうやって実現したのでしょうか?答えは埋め込み型です.STLでは、ほとんどのコンテナで反復器の埋め込み型を定義する必要があります.以下はlistの定義で、簡略化されています.
class MyAlloc{  
};

template<class T>
struct List_iterator{
  typedef T value_type;  //list         
  ...
};

template <class T, class Alloc = MyAlloc>
class list{
public:
  typedef List_iterator<T>  iterator;  //list   
  ...
};
        
リスト反復器が指すデータ型は、以下のように抽出することができる.
template<class I>
struct Iterator_traits{ //     
	typedef typename I::value_type value_type;
};

Iterator_traits<list<int>::iterator>::value_type x = 1;
        
この方法では、埋め込み型を定義する別の反復器を抽出するしかありませんが、原生ポインタであれば、埋め込み型の違いはありませんか?例えばvector容器は、原生ポインタで反復器を作っています.次のように定義します.
class MyAlloc{  
};

template <class T, class Alloc = MyAlloc>
class vector : 
{
public:
  typedef T value_type;    //    
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;  //vector    ,     
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  ...
};

      
テンプレート特化がついに登場し、以下にオリジナルポインタのサポートを加え、テンプレート特化技術を使用し、汎化設計に特化バージョンを加えた.この技術もSTLの核心的な鍵である.
template<class I>
struct Iterator_traits{
	typedef typename I::value_type value_type;
};
//       
template<class T>
struct Iterator_traits<T*>{
	typedef T value_type;
};
//        
template<class T>
struct Iterator_traits<const T*>{
	typedef T value_type;
};

    
以下に完全なコードを示し、VS 2008でテストに合格した.
#include <iostream>
#include <vector>
#include <list>
using namespace std;

//   
template<class I>
struct Iterator_traits{
	typedef typename I::value_type value_type;
};
//       
template<class T>
struct Iterator_traits<T*>{
	typedef T value_type;
};
//        
template<class T>
struct Iterator_traits<const T*>{
	typedef T value_type;
};

#define VALUE_TYPE(I) Iterator_traits<I>::value_type()

//            
template <class Iter1, class Iter2>
inline void iter_swap(Iter1 a, Iter2 b) {
 _iter_swap(a, b, VALUE_TYPE(Iter1)); //VALUE_TYPE         
}
//       
template <class Iter1, class Iter2, class T>
inline void _iter_swap(Iter1 a, Iter2 b, T) {
  T tmp = *a;
  *a = *b;
  *b = tmp;
}
//    
int main()
{
	int a = 1, b = 2;
	iter_swap(&a,&b);
	cout<<a<<' '<<b<<endl;  //2 1
	
	list<int> l;
	l.push_back(3);
	l.push_back(4);
	iter_swap(l.begin(),++l.begin());
	cout<<*(l.begin())<<' '<<*(++l.begin())<<endl; //4 3

	Iterator_traits<int *>::value_type w = 5;       //  
	Iterator_traits<const int*>::value_type  x = 6; //  
        Iterator_traits<vector<int>::iterator>::value_type y = 7; //vector   
	Iterator_traits<list<int>::iterator>::value_type z = 8;   //list   
	cout<<w<<' '<<x<<' '<<y<<' '<<z<<endl; //5 6 7 8
	return 0;
}
        
本稿では,テンプレートの特化とともに,STL反復器が実現するもう一つの重要な技術である埋め込み型を紹介した.テンプレートの実パラメトリック推定メカニズムについて説明します.
私はブログの文章の著作権を享有して、転載して出典を明記してくださいhttp://blog.csdn.net/wuzhekai1985