type trait用于平行类层次中的参数类型检查

 

什么是平行类层次

如以下UML图所示。Animal都需要吃(eat)(Food),  Dog只吃狗食(DogFood), Cat只吃猫食(CatFood).

狗吃猫食?

Dog dog;
CatFood catFood;
dog.eat(catFood); // 狗吃了猫食。

解决方案

方案1:覆盖(override)eat函数

覆盖函数可定义如下:

void Dog::eat(const Food& food)
{
If (food is NOT an instance of DogFood)
狗吠 and return;
……
}

优点:狗不会误食猫食了。

缺点:只有在狗开始吃时(运行时)才知道食物是否可吃。在某些情况下,为时已晚。

方案2:添加额外接口

DogCat添加额外接口来吃食,如下图所示。从今以后Dog只能通过eatDogFood接口来吃食,Cat只能通过eatCatFood接口来吃食。

优点:狗吃得安心了,不再担心被喂猫食。

缺点:在DogCat中个增加了一个接口,从而增加了记忆和使用的负担。

方案3Type trait

我们可以定义一个模板函数(template function),主人一律通过此模板函数来给Animal喂食,而无需分别对待具体Animal,如猫、狗等。模板函数可定义如下:

template<class AnAnimal, class AFood>
void feed(const AnAnimal& animal, const AFood& food)
{
animal.eat(food);
}

显然需要对以上模板参数AnAnimalAFood进行一定的约束,才能满足我们的需求。首先,AnAnimal必须是一种真正的Animal (继承于Animal)。一个简单赋值可达到目的。赋值语句如下:

const Animal& realAnimal = animal; // 编译错误如果animal不是继承于Animal.

其次,AFood必须是AnAnimal对应的食物类型, Animal 对应 Food, Dog对应DogFood, Cat对应CatFood. 换句话说,AFoodAnAnimal来决定,AFood可以看成是AnAnimal的一个特性。这不正是type trait所做的是吗!定义type trait如下:

template<class AnAnimal> struct AnimalFoodTrait;
template<> struct AnimalFoodTrait<Animal> {typedef Food AFood;};
template<> struct AnimalFoodTrait<Dog> {typedef DogFood AFood;};
template<> struct AnimalFoodTrait<Cat> {typedef CatFood AFood;};

最后,利用这些type trait我们可以修改那个模板函数,让其只带一个AnAnimal模板参数,AFood参数由AnAnimal推导出。

所以,最终模板函数定义为:

template<class AnAnimal>
void feed(const AnAnimal& animal, const AnimalFoodTrait<AnAnimal>::AFood& food)
{
const Animal& realAnimal = animal;
realAnimal.eat(food);
}

利用这个修改过的模板函数,主人立刻(编译期)就可以知道是否喂错了食。以狗吃猫食为例:

Dog dog;
CatFood catFood;
feed(dog, catFood); // 狗吠,CatFood不能转化成DogFood.

你可能感兴趣的:(C++)