type trait平行クラス階層でのパラメータタイプチェック

5453 ワード


平行クラス階層とは
以下のUML図に示すように.Animalはすべて(eat)食(Food)、Dogは犬食(DogFood)、Catは猫食(CatFood)しか食べない.
犬は猫の餌を食べますか.
Dog dog;
CatFood catFood;
dog.eat(catFood); // 。

ソリューション
シナリオ1:オーバーライドeat関数
上書き関数は次のように定義できます.
void Dog::eat(const Food& food)
{
If (food is NOT an instance of DogFood)
and return;
……
}

長所:犬は猫の餌を誤って食べない.
欠点:犬が食べ始めたとき(運行時)だけ食べ物が食べられるかどうかを知る.場合によっては、もう遅い.
シナリオ2:追加インタフェースの追加
下の図に示すように、DogとCatに追加のインタフェースを追加して食事をします.これからはDogはeatDogFoodインタフェースでしか食べられず、CatはeatCatFoodインタフェースでしか食べられない.
良い点:犬は安心して食べて、猫に餌を与えられる心配はありません.
欠点:DogとCatにインタフェースを追加することで、記憶と使用の負担が増加します.
シナリオ3:Type trait
猫や犬などの特定のAnimalを別々に扱う必要がなく、飼い主がこのテンプレート関数によってAnimalに餌を与えるテンプレート関数(template function)を定義することができます.テンプレート関数は次のように定義できます.
template<class AnAnimal, class AFood>
void feed(const AnAnimal& animal, const AFood& food)
{
animal.eat(food);
}

以上のテンプレートパラメータAnAnimalとAFoodを一定の制約で満たす必要があることは明らかです.まず、Animalは真のAnimal(Animalに継承)でなければならない.単純な付与は目的を達成することができる.付与文は次のとおりです.
const Animal& realAnimal = animal; //       animal     Animal.

次に、AFoodは、AnAnAnimal対応の食品タイプ、すなわちAnimal対応Food、Dog対応DogFood、Cat対応CatFoodである必要がある.言い換えれば、AFoodはAnAnAnimalによって決定され、AFoodはAnAnAnimalの特性と見なすことができる.これは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を用いて,そのテンプレート関数を1つのAnAnAnimalテンプレートパラメータのみを持つように修正することができ,AFoodパラメータをAnAnAnimalから導出した.
したがって、最終テンプレート関数は次のように定義されます.
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.