C++テンプレート-value traits

2458 ワード

前の文章はtype traitsを使っていますが、実はtraitsにはvalue traitsもあります.
累積関数をもう一度見てみましょう.
template<typename T>
struct traits;

template<>
struct traits<char>
{
	typedef int AccuT;
};

template<>
struct traits<int>
{
	typedef int AccuT;
};

template<class T>
typename traits<T>::AccuT accum3(const T* ptr, int len)
{
	traits<T>::AccuT total = traits<T>::AccuT();

	for (int i = 0; i < len; i++)
	{
		total += *(ptr + i);
	}

	return total;
}

この行のコードに注意してください.
traits::AccuT total = traits::AccuT();
AccuTがint,floatなどのタイプであれば,int(),float()は0に初期化されて問題ないが,万一対応するタイプが()初期化できなかったら?
このときvalue traitsを使い、int traitsを次のように変更できます.
template<>
struct traits<int>
{
	typedef int AccuT;
	static AccuT const Zero = 0;
};

このtraitsにはタイプと値があります.
次に、累積関数を次のように変更します.
template<class T>
typename traits<T>::AccuT accum3(const T* ptr, int len)
{
	traits<T>::AccuT total = traits<T>::Zero;

	for (int i = 0; i < len; i++)
	{
		total += *(ptr + i);
	}

	return total;
}

これで初期化の問題を解決できます.そして変動があってもtraitsの中のZeroを修正するだけです.
 
しかし、すべてのタイプがクラスで初期化できるわけではありません.例えば、int traitsの戻り値タイプをdoubleに変更します.
template<>
struct traits<int>
{
	typedef double AccuT;
	static AccuT const Zero = 0;
};

このようにコンパイラは直接エラーを報告します(vs 2012)、
error C2864: 'traits::Zero' : only static const integral data members can be initialized within a class
クラスの外でこの値を初期化する人もいます.例えば、double const traits::Zero=0;これも一つの方法だ.しかし、これは良い方法ではないような気がします.より一般的にはtraits内部で静的関数を作り、静的変数ではなく関数を累積します.コード:
template<typename T>
struct traits;

template<>
struct traits<char>
{
	typedef int AccuT;
	static AccuT const Zero = 0;
};

template<>
struct traits<int>
{
	typedef double AccuT;
	static AccuT Zero(){ return 0.0; };
};

template<class T>
typename traits<T>::AccuT accum3(const T* ptr, int len)
{
	traits<T>::AccuT total = traits<T>::Zero();

	for (int i = 0; i < len; i++)
	{
		total += *(ptr + i);
	}

	return total;
}


int _tmain(int argc, _TCHAR* argv[])
{
	int sz[] = {1, 2, 3};
	traits<int>::AccuT avg = accum3(sz, 3) / 3;

	return 0;
}