配列割り当てポリシーの実装が満たすべき要件について述べる。
ポリシーは、2相テンプレートと状態を持ち得るポリシークラスで構成される。フロントエンドのクラス名をpolicy_sample、バックエンド導出のために与えられた操作対象の型をT、そのサイズをN(= sizeof()とすると、バックエンドのクラスは
T)
typenamepolicy_sample::templatebind<N>::type
という記述で得られる。以降、バックエンドのクラス名をbackendと表記する。
backendは次のpublicなメンバ型を持つ。
size_type
割り当てる領域の大きさを表現可能な符号なし整数型。
initializer
backendに対する初期化オブジェクトの型。backendのオブジェクトは初期化オブジェクトを用いて構築できる。ポリシークラス実装者は、backendのオブジェクトを動的な値で初期化する手段としてinitializerに任意の実装を与えてよい。
backendは次のprotectedなメンバ関数を持つ。以下の記述では非静的メンバ関数での実装を仮定しているが、可能であれば静的メンバ関数で実装しても構わない。
backend()
引数なしでオブジェクトを構築する。
backend(U init)
初期化オブジェクトinitを用いてオブジェクトを構築する。引数の型Uはinitializerもしくはconst initializer&のどちらかである。
backend(const backend& rhs)
同型のオブジェクトrhsを用いてオブジェクトを構築する。
~backend()
オブジェクトを破壊する。
backend& operator=(const backend&)
存在しない。
void swap(backend& rhs)
自身とrhsの状態を入れ換える。
initializer get_initializer() const
初期化オブジェクトを返す。
size_type max_size() const
割り当て可能な記憶領域の大きさの最大値を返す。swapで全状態を交換したときを除いて、ポリシーオブジェクトの生存期間全体を通じて戻り値は一定であること。
void* allocate(size_type n)
型Tのオブジェクトn個を格納するのに充分な大きさを持つ記憶領域を割り当て、その先頭へのポインタを返す。nがmax_sizeの戻り値を越えていたときの動作は未定義。
戻り値pは次のすべての条件を満たさなければならない。
T* としたとき、[q = static_cast<T*>(p), q)の範囲を正当に参照アクセスできること: すなわち、範囲内の領域へのアクセス権が与えられており、型Tについてのアラインメントが適切に取られていること
q + n
pが指す領域がプログラム内で一意であること
これらの条件は、nが0であっても満たされなければならない。領域サイズが0であれば、条件1はpがいかなる値であっても満たされるが、条件2には留意する必要がある。例えばヌルポインタを返すべきではない: このようにすると、を2度呼んだときに条件2に違反することになる。
allocate(0)
条件2は、例えば次のように実装することで満たせる。
void* allocate(size_type n)
{
if (n == 0) n = 1;
void* const p = std::malloc(n * sizeof_T_);
// ...
}
void deallocate(void* p)
引数pで示される記憶領域を開放する。pがallocateで割り当てられた記憶領域でないときの動作は未定義。
ポリシークラスが通常満たすべき例外安全性と計算量の条件は次の通り。
表 6.2. 配列割り当てポリシーの例外安全性と計算量
| 操作 | 例外安全性 | 計算量 |
|---|---|---|
| すべてのコンストラクタ | strong | 定数 |
| デストラクタ | nothrow | 定数 |
swap | nothrow | 定数 |
get_initializer | strong | 定数 |
allocate(n) | strong | nに対して線形 |
deallocate(p) | nothrow | pで示される記憶領域の大きさに対して線形 |
max_size | nothrow | 定数 |