単純なアロケータの実装
template <class T>
struct mini_allocator {
typedef T value_type;
T* allocate(size_t n) {
return reinterpret_cast<T*>(new char[n*sizeof(T)]);
}
void deallocate(T* p, size_t n) {
delete[] reinterpret_case<char*>(p);
}
};
バイト単位(char型)でメモリ領域を確保・開放するだけのもの。
実際にはnew
、delete
である必要はなく、スタック領域に確保したものを返す実装でも構わないことになる。
スタックが巨大なマシンであれば、ヒープ領域から確保せずにスタック領域だけで済ませることも可能なのか気になる。
多分できる。
rebind
アロケータはテンプレート引数によって確保したい型を指定する。 そのため、別の型でメモリ領域を確保したいときは別のアロケータを新たに定義する必要がある。
あるアロケータの正しい実装obj_allocator<T>
があり、Foo
クラスを確保するアロケータFoo_Alloc = obj_allocator<Foo>
を定義する。
Bar
クラスに対するアロケータの定義を行いたい時にrebind
を使うことができる。
rebind
は以下のように定義されている。
template <class T>
class allocator {
// ...
public:
template <class U>
struct rebind {
typedef allocator<U> other;
};
// ...
};
rebind
を使うと、obj_allocator<T>
を複数箇所で使うことなく、同じアロケータで複数の型に対するアロケータを作成することができる。
typedef obj_allocator<Foo> foo_allocator_type;
typedef typename foo_allocator_type::template rebind<Bar>::other bar_allocator_type;
アロケータの実装を別のものに変えたいときは、foo_allocator_type
の定義1つを変えれば良い。
感想
rebind
についてあまり理解できていなかったなと思った。
C++のテンプレートは便利だけど、考えることが多すぎるのと抽象的な部分が多くて難しい。
C++コンパイル時Cコンパイラみたいな面白挙動ができるのも納得。
追記
テンプレート引数でアロケータの型が渡される時にrebind
が活躍する。
template <class T, class Allocator>
class Foo;
を使う場合は、
Foo<int, std::allocator<int> > val;
のように宣言する。
Foo
クラスの内部実装からはT
のアロケータであることしかわからず、アロケータ自体がどのような実装かまでは判別できない。
std::allocator
かもしれないし、mini_allocator
かもしれない。
rebind
を使うことで、アロケータの実装が取り出せる。
つまり、同じアロケータを使って別の型のアロケータを定義できる。
参考文献
プログラミング言語C++ 第4版 34.4 アロケータ