Traitsテクノロジーの初探査
5603 ワード
概要:traitsは、Generic Programmingにおいて広く用いる特性抽出技術であり、異なるタイプを同じ操作に用いることができる、あるいは異なるタイプに対して異なる実現を提供するためによく用いられる.traitsは、実装の過程で、enumtypedeftemplate(partial)specializationの3つのC++の基本的な特性を使用することが多い.enumは、異なるタイプの間で変化する表示を1つに統一するために使用され、C++でdefineの代わりにクラスで使用されることが多い.enumをクラスのdefineと呼ぶことができる.typedefはあなたのテンプレートクラスのサポート特性の形式を定義するために使用され、あなたのテンプレートクラスは何らかの形式である特性をサポートしなければならない.そうしないと、タイプ抽出器traitsは正常に動作しない.ここを見ると、過酷だと思うかもしれません.実際にはそうではない、ある特性をサポートしないこと自体もサポートする方法である(例2を参照して、ある特性に対するサポートとサポートしないことをそれぞれ示す_xtrue_typeと_xfalse_typeの2つの標識を定義).template(partial)specializationは、特定のタイプの正しいバージョンまたはより適切なバージョンを提供するために使用される.以上のいくつかの簡単な技術によって、traitsを利用してクラスで定義された特性を抽出し、異なる特性に基づいて異なる実現を提供することができる.特性の定義から抽出、traitsの実際の使用までをtraits技術と総称することができますが、この定義はtraitsを複雑にしすぎ、traitsの定義を特性抽出に限定したいと思っています.この定義はtraitsをより簡単に、理解しやすいからです.例えば、上記のように、traitsは、異なるタイプの実装を提供するために使用することができるが、以下では、この実装方法を2つの例で説明する.Example 1:あるクラスに対してすべてのタイプ(通常のint/long...、cloneメソッドを含む複雑なタイプのCComplexObject、およびそのクラスから派生するクラス)を操作できる関数cloneを設計する必要があると仮定し、以下、まずOOの方法で解決策を考える.前の条件を見て、最初にあなたの頭の中に飛び込んだのはInterface、pure virtual functionなどに違いない.私たち自身が設計したクラスCComplexObjectにとって、これは問題ではありませんが、基本データ型については?cloneメソッドを提供していない複雑なタイプはありますか?(Javaがマルチeasyであれば、すべてのクラスがObjectから派生するのをデフォルトとし、Objectはデフォルトのcloneメソッドを提供しているが、クラスが本当にcloneをサポートするにはimplements Cloneableが必要であるため、同様にここで発生するトラブルを避けることはできないと考えるかもしれない).次のようなソリューションがあります.
しかし、テストさえすれば、このコードはコンパイルできません.どうしてこんなことになったの?理由は簡単である:cloneメソッドを実現していない非Clonableクラスまたは基本タイプに対してpObj->cloneという文は不法である.では、上記の難題をどのように解決すればいいのでしょうか.コンパイルできないコードは、私たちのコードをコンパイルするには、非Clonableクラスや基本タイプのコードにpObj->cloneを出現させることはできません.つまり、異なるタイプに対して異なる実装を提供する必要があります.これを実現するために、クラスがCloableクラスであるか否かを示すためにenumでtraitを定義し、元のテンプレートクラスの内部にtraits抽出クラスTraitsを導入し、そのクラスをspecilizingすることで、異なるtraitに基づいて異なる実装を提供することができる.具体的には、次のようになります.
コンパイルを実行すると、上のプログラムは次の結果を出力します.
これは、入力isClonableテンプレートパラメータに基づいてテンプレートインスタンスに対して異なる動作を選択することに成功し、インタフェースが同じであることを保証する場合、異なるタイプに対して異なる実装を提供したことを示す.Example 2:上記の例をいくつか制限し、clone操作が基本タイプとCComplexObjectとその派生クラスにのみ関連していると仮定すると、次の解法をさらに与えることができます.
これにより、XContainerには、すべての基本タイプおよびCComplexObjectクラスが使用できるようになる.
template <typename T, bool isClonable>
class XContainer
{
...
void clone(T* pObj)
{
if (isClonable)
{
pObj->clone();
}
else
{
//... non-Clonable algorithm ...
}
}
};
しかし、テストさえすれば、このコードはコンパイルできません.どうしてこんなことになったの?理由は簡単である:cloneメソッドを実現していない非Clonableクラスまたは基本タイプに対してpObj->cloneという文は不法である.では、上記の難題をどのように解決すればいいのでしょうか.コンパイルできないコードは、私たちのコードをコンパイルするには、非Clonableクラスや基本タイプのコードにpObj->cloneを出現させることはできません.つまり、異なるタイプに対して異なる実装を提供する必要があります.これを実現するために、クラスがCloableクラスであるか否かを示すためにenumでtraitを定義し、元のテンプレートクラスの内部にtraits抽出クラスTraitsを導入し、そのクラスをspecilizingすることで、異なるtraitに基づいて異なる実装を提供することができる.具体的には、次のようになります.
#include <iostream>
using namespace std;
class CComplexObject // a demo class
{
public
:
void clone() {
cout << "in clone" << endl;
}
};
// Solving the problem of choosing method to call by inner traits class
template <typename T, bool isClonable>
class XContainer
{
public:
enum {
Clonable = isClonable
};
void clone(T* pObj)
{
Traits<isClonable>().clone(pObj);
}
template <bool flag>
class Traits
{
};
template <>
class Traits<true>
{
public:
void clone(T* pObj)
{
cout << "before cloning Clonable type" << endl;
pObj->clone();
cout << "after cloning Clonable type" << endl;
}
};
template <>
class Traits<false>
{
public:
void clone(T* pObj)
{
cout << "cloning non Clonable type" << endl;
}
};
};
void main()
{
int* p1 = 0;
CComplexObject* p2 = 0;
XContainer<int, false> n1;
XContainer<CComplexObject, true> n2;
n1.clone(p1);
n2.clone(p2);
}
コンパイルを実行すると、上のプログラムは次の結果を出力します.
cloing something non Clonable
before doing something Clonable
in clone
after doing something Clonable
これは、入力isClonableテンプレートパラメータに基づいてテンプレートインスタンスに対して異なる動作を選択することに成功し、インタフェースが同じであることを保証する場合、異なるタイプに対して異なる実装を提供したことを示す.Example 2:上記の例をいくつか制限し、clone操作が基本タイプとCComplexObjectとその派生クラスにのみ関連していると仮定すると、次の解法をさらに与えることができます.
#include <iostream>
using namespace std;
struct __xtrue_type { }; // define two mark-type
struct __xfalse_type { };
class CComplexObject // a demo class
{
public:
virtual void clone() {
cout << "in clone" << endl;
}
};
class CDerivedComplexObject : public CComplexObject // a demo derived class
{
public:
virtual void clone() {
cout << "in derived clone" << endl;
}
};
// A general edtion of Traits
template <typename T>
struct Traits
{
typedef __xfalse_type has_clone_method;
// trait 1: has clone method or not? All types defaultly has no clone method.
};
// Specialized edtion for ComplexObject
template <>
struct Traits<CComplexObject>
{
typedef __xtrue_type has_clone_method;
};
template <typename T>
class XContainer
{
template <typename flag>
class Impl
{
};
template <>
class Impl <__xtrue_type>
{
public:
void clone(T* pObj)
{
pObj->clone();
}
};
template <>
class Impl <__xfalse_type>
{
public:
void clone(T* pObj)
{
}
};
public:
void clone(T* pObj)
{
Impl<Traits<T>::has_clone_method>().clone(pObj);
}
};
void main()
{
int* p1 = 0;
CComplexObject c2;
CComplexObject* p2 = &c2;
CDerivedComplexObject c3;
CComplexObject* p3 = &c3;
// you must point to a derived object by a base-class pointer,
//it's a little problem
XContainer<int> n1;
XContainer<CComplexObject> n2;
XContainer<CComplexObject> n3;
n1.clone(p1);
n2.clone(p2);
n3.clone(p3);
}
これにより、XContainerには、すべての基本タイプおよびCComplexObjectクラスが使用できるようになる.