trait类——类型萃取

版权声明:本文为博主原创文章,如有需要,可以随意转载,请注明出处。 https://blog.csdn.net/xunye_dream/article/details/82946580

       这里实现一个通用的计算数组类的数据求和函数。C++中用模板实现多态属于静态多态,trait类就是做为模板参数以实现静态多态。根据不同的模板参数类型,以实现不同模板数据类型所需的功能。其实说白了,就是利用模板的一些基本规则,将其合理的组合起来,以达到根据类型不同时函数的操作有细微个性的差别。

依据TDD开发模式,先写测试用例。

#include <iostream>
#include <assert.h>
#include "accum.hpp"

using namespace std;

int main(int argc, char **argv)
{
	int arr[5] = {1, 2, 3, 4, 5};

	cout << accum(arr, arr+5) << endl;

	assert(accum(arr, arr+5) == 15);

	return 0;
}

1、没有使用trait的实现方式

template <typename T>
inline T& accum(T const* beg, T const* end)
{
    T total = T();    //初始化,假定初始值为0
    while (beg != end)
    {
        total += *beg;
        ++beg;
    }

    return total;
}

上述代码的缺点:1、初始化不一定是初始值为0;2、T类型求和可能导致类型溢出。

2、解决类型溢出问题,采用类型关联方式实现

//通过特化形式,将类型关联起来,以扩大类型的范围

template<typename T>
struct AccumulateTraits;     //trait模板

template<>                   //模板特化,类型关联
struct AccumulateTraits<char>
{
	typedef int AccT;
};

template<>
struct AccumulateTraits<short>
{
	typedef int AccT;
};

template<>
struct AccumulateTraits<int>
{
	typedef long AccT;
};

template<>
struct AccumulateTraits<unsigned int>
{
	typedef unsigned long AccT;
};

template<>
struct AccumulateTraits<float>
{
	typedef double AccT;
};
#include "traits.hpp"

template<typename T>
inline typename AccumulateTraits<T>::AccT accum(T const* beg, T const* end)
{
    //使用模板类型中的类型时,一定要typename修饰
	typedef typename AccumulateTraits<T>::AccT AccT;

	AccT total = AccT();
	while (beg != end)
	{
		total += *beg;
		++beg;
	}

	return total;
}

3、在2的基础上解决初始化不为0问题(value trait——值萃取)

template<typename T>
struct AccumulateTraits;     //trait模板

template<>                   //模板特化,类型关联
struct AccumulateTraits<char>
{
	typedef int AccT;
    //通过静态成员函数来初始化为零
	static AccT zero()
	{
		return 0;
	}
};

template<>
struct AccumulateTraits<short>
{
	typedef int AccT;
	static AccT zero() 
	{
		return 0;
	}
};

template<>
struct AccumulateTraits<int>
{
	typedef long AccT;
	static AccT zero() 
	{
		return 0;
	}
};

template<>
struct AccumulateTraits<unsigned int>
{
	typedef unsigned long AccT;
	static AccT zero() 
	{
		return 0;
	}
};

template<>
struct AccumulateTraits<float>
{
	typedef double AccT;
	static AccT zero() 
	{
		return 0.0;
	}
};
#include "traits.hpp"

template<typename T>
inline typename AccumulateTraits<T>::AccT accum(T const* beg, T const* end)
{
	typedef typename AccumulateTraits<T>::AccT AccT;

	AccT total = AccumulateTraits<T>::zero();
	while (beg != end)
	{
		total += *beg;
		++beg;
	}

	return total;
}

上述代码的缺点是:类型绑定过死板,已经绑定不能被修改,有时也需要对类型进行自行定制。

4、采用参数化的形式进行类型萃取(trait)

template<typename T, typename AT = AccumulateTraits<T> >
struct Accum
{
	typedef typename AT::AccT AccT;
	static AccT accum(T const* beg, T const* end)
	{
		AccT total = AT::zero();
		while (beg != end)
		{
			total += *beg;
			++beg;
		}

		return total;
	}
};
//test case
#include <iostream>
#include <assert.h>
#include "accum.hpp"

using namespace std;

int main(int argc, char **argv)
{
	int arr[5] = {1, 2, 3, 4, 5};

	assert(Accum<int>::accum(arr, arr+5) == 15);

	return 0;
}

总之,类型萃取(trait)在处理类型关联有大的作用。

猜你喜欢

转载自blog.csdn.net/xunye_dream/article/details/82946580