アンドレイ・アレキサンドレスクのこの本で、あなたの質問に対する最良の答えが見つかると思います。ここでは、簡単な概要を説明します。うまくいけば、それが役立つでしょう。
トレイトクラスは、通常、タイプを他のタイプまたは定数値に関連付けてそれらのタイプの特性を提供するメタ関数となることを目的としたクラスです。つまり、型のプロパティをモデル化する方法です。このメカニズムは通常、テンプレートとテンプレートの特殊化を利用して、関連付けを定義します。
template<typename T>
struct my_trait
{
typedef T& reference_type;
static const bool isReference = false;
// ... (possibly more properties here)
};
template<>
struct my_trait<T&>
{
typedef T& reference_type;
static const bool isReference = true;
// ... (possibly more properties here)
};
上記のトレイトメタ関数my_trait<>
は、参照型T&
と定数ブール値を、それ自体が参照ではないfalse
すべての型に関連付けます。一方、参照型と定数ブール値を参照であるすべての型に関連付けます。T
T&
true
T
たとえば、次のようになります。
int -> reference_type = int&
isReference = false
int& -> reference_type = int&
isReference = true
コードでは、上記を次のようにアサートできます(以下の4行すべてがコンパイルされます。つまり、最初の引数で表現された条件static_assert()
が満たされます)。
static_assert(!(my_trait<int>::isReference), "Error!");
static_assert( my_trait<int&>::isReference, "Error!");
static_assert(
std::is_same<typename my_trait<int>::reference_type, int&>::value,
"Error!"
);
static_assert(
std::is_same<typename my_trait<int&>::reference_type, int&>::value,
"Err!"
);
ここでは、標準テンプレートを使用したことがわかります。これは、それ自体が1つではなく2つの型引数をstd::is_same<>
受け入れるメタ関数です。ここでは、物事が任意に複雑になる可能性があります。
std::is_same<>
はヘッダーの一部ですが、クラステンプレートがメタ述語として機能する(つまり、 1つのテンプレートパラメーターtype_traits
を受け入れる)場合にのみ、クラステンプレートを型特性クラスと見なす人もいます。しかし、私の知る限り、用語は明確に定義されていません。
C ++標準ライブラリでの特性クラスの使用例については、入出力ライブラリと文字列ライブラリがどのように設計されているかを見てください。
ポリシーは少し異なります(実際にはかなり異なります)。これは通常、いくつかの異なる方法で実現される可能性のある特定の操作に関する別のジェネリッククラスの動作を指定するクラスを意味します(したがって、その実装はポリシークラスに任されます)。
たとえば、ジェネリックスマートポインタクラスは、参照カウントの処理方法を決定するためのテンプレートパラメータとしてポリシーを受け入れるテンプレートクラスとして設計できます。これは単なる架空の、過度に単純化された、説明的な例なので、抽象化してみてください。この具体的なコードから、メカニズムに焦点を当てます。
これにより、スマートポインターの設計者は、参照カウンターの変更をスレッドセーフな方法で実行するかどうかについて、ハードコードされたコミットメントを行うことができなくなります。
template<typename T, typename P>
class smart_ptr : protected P
{
public:
// ...
smart_ptr(smart_ptr const& sp)
:
p(sp.p),
refcount(sp.refcount)
{
P::add_ref(refcount);
}
// ...
private:
T* p;
int* refcount;
};
マルチスレッドのコンテキストでは、クライアントは、参照カウンターのスレッドセーフなインクリメントとデクリメントを実現するポリシーでスマートポインターテンプレートのインスタンス化を使用できます(ここではWindowsプラットフォームを想定)。
class mt_refcount_policy
{
protected:
add_ref(int* refcount) { ::InterlockedIncrement(refcount); }
release(int* refcount) { ::InterlockedDecrement(refcount); }
};
template<typename T>
using my_smart_ptr = smart_ptr<T, mt_refcount_policy>;
一方、シングルスレッド環境では、クライアントは、カウンターの値を単純に増減するポリシークラスを使用してスマートポインターテンプレートをインスタンス化できます。
class st_refcount_policy
{
protected:
add_ref(int* refcount) { (*refcount)++; }
release(int* refcount) { (*refcount)--; }
};
template<typename T>
using my_smart_ptr = smart_ptr<T, st_refcount_policy>;
このように、ライブラリ設計者は、パフォーマンスと安全性の間で最良の妥協点を提供できる柔軟なソリューションを提供しました(「使用しないものにお金を払わない」)。