More C++ Idioms/整数から型(Int-To-Type)
整数から型(Int-To-Type)
[編集]意図
[編集]- コンパイル時に整数定数を型として扱う。
- 整数定数値に基づいて静的な呼び分け(dispatch)を実現する。
別名
[編集]整数定数ラッパ(Integral constant wrappers)
動機
[編集]C++ における関数の多重定義は型が異なることに基づいており、コンパイル時整数定数を関数の多重定義の解決に利用することができない。整数定数に基づいて静的な呼び分け(dipatch)を実現する方法が少なくとも 2 つある。一つは enable-if イディオムであり、もう一つが以下で記述する整数から型(Int-To-Type)イディオムである。
解法とサンプルコード
[編集]Andrei Alexandrescu によって Dr. Dobb's Journal で初めて記述された単純なテンプレートがこのイディオムの解法を提供する。
template <int I>
struct Int2Type
{
enum { value = I };
};
上記テンプレートはテンプレートのインスタンス化を用いることで異なる値に対して異なる型を作成する。例えば、Int2Type<5> は Int2Type<10> と異なる型である。さらに、整数のパラメータは関連づけられた定数 value に保存されている。整数定数それぞれが異なる型を生み出すため、この単純なテンプレートを以下のように整数定数に基づく静的な呼び分け(dispatch)に用いることができる。
Array クラスを固定長の配列をカプセル化するクラスで TR1 にある標準 array クラスによく似たクラスとする。実際には、我々の Array クラスは標準の TR1 array クラスの派生クラスとして実装されており違いは sort 関数のみである。
性能に対する最適化を達成するため、配列のサイズによってコンパイル時に sort 関数の呼び分け(dispatch)をする。例えば、配列のサイズが 0 あるいは 1 の場合はソートは何もしない。同様に、サイズが 50 より小さな配列は挿入ソートアルゴリズムによってソートされ、より大きな配列はクイックソートアルゴリズムによってソートされる。なぜなら小さなデータサイズに対しては、挿入ソートアルゴリズムの方がクイックソートアルゴリズムよりも効率的である場合が多いからである。このソートアルゴリズムの選択は実行時の if によって自明に実行可能である点に注意しよう。しかし、以下のように整数から型(Int-To-Type) イディオムによってコンパイル時に同様の効果を得ることができる。
#include <iostream>
#include <tr1/array>
template <int I>
struct Int2Type
{
enum { value = I };
};
template <class T, unsigned int N>
class Array : public std::tr1::array <T, N>
{
enum AlgoType { NOOP, INSERTION_SORT, QUICK_SORT };
static const int algo = (N==0) ?NOOP :
(N==1) ?NOOP :
(N<50) ?INSERTION_SORT : QUICK_SORT;
void sort (Int2Type<NOOP>) { std::cout << "何もしない\n"; }
void sort (Int2Type<INSERTION_SORT>) { std::cout << "挿入ソート\n"; }
void sort (Int2Type<QUICK_SORT>) { std::cout << "クイックソート\n"; }
public:
void sort()
{
sort (Int2Type<algo>());
}
}
int main(void)
{
Array<int, 1> a;
a.sort(); // 何もしない!
Array<int, 400> b;
b.sort(); // クイックソート
}
利便性を向上するために、Int2Type にいくつか関連する型と定数を定義することができる。例えば、列挙型 value を型に関連づけられた整数定数を取得するために利用できる。また、Int2Type<7>::next が Int2Type<9>::previous と同じ型になるような next と previous のような他の typedef を他の型を 順番に 探すために利用することができる。
template <int I>
struct Int2Type
{
enum { value = I };
typedef int value_type;
typedef Int2Type<I> type;
typedef Int2Type<I+1> next;
typedef Int2Type<I-1> previous;
};
既知の利用
[編集]関連するイディオム
[編集]References
[編集][1] Generic<Programming>: Mappings between Types and Values -- Andrei Alexandrescu