C++ の名前付き要件: Allocator
オブジェクトへのアクセス/アドレス指定、アロケーション/デアロケーション、構築/破棄の戦略をカプセル化します。
std::string、std::vector、およびすべてのコンテナ (std::arrayを除く(C++11以降)とstd::inplace_vectorを除く(C++26以降) から、std::shared_ptr (およびstd::function(C++17まで)) まで、ストレージをアロケートまたは解放する必要があるすべての標準ライブラリコンポーネントは、次の要件を満たすクラス型オブジェクトであるAllocatorを介してこれを行います。
多くのアロケータ要件の実装はオプションです。なぜなら、すべてのAllocatorAwareContainerはstd::allocator_traitsを介して間接的にアロケータにアクセスし、std::allocator_traitsがそれらの要件のデフォルト実装を提供するからです。
目次 |
[編集] 要件
以下を考えます。
-
T、非const、非参照型(C++11まで)非constオブジェクト型(C++11以降)(C++17まで)cv修飾なしオブジェクト型(C++17以降)、 -
A、型T用のAllocator型、 - a、型
Aのオブジェクト、 -
B、(Aをリバインドして得られる) あるcv修飾なしオブジェクト型Uに対応するAllocator型、 - b、型
Bのオブジェクト、 - p、std::allocator_traits<A>::pointer型の値。std::allocator_traits<A>::allocate()を呼び出して取得される、
- cp、std::allocator_traits<A>::const_pointer型の値。pからの変換によって取得される、
- vp、std::allocator_traits<A>::void_pointer型の値。pからの変換によって取得される、
- cvp、std::allocator_traits<A>::const_void_pointer型の値。cpまたはvpからの変換によって取得される、
- xp、あるcv修飾なしオブジェクト型
Xへの逆参照可能なポインタ、 - r、式*pによって得られる型
Tの左辺値、 - n、std::allocator_traits<A>::size_type型の値。
| 型ID | エイリアス型 | 要件 |
|---|---|---|
A::pointer (オプション) |
(未指定)[1] | |
A::const_pointer (オプション) |
(未指定) |
|
A::void_pointer (オプション) |
(未指定) |
|
A::const_void_pointer (オプション) |
(未指定) |
|
A::value_type
|
T
|
|
A::size_type (オプション) |
(未指定) |
|
A::difference_type (オプション) |
(未指定) |
|
A::template rebind<U>::other(オプション)[2] |
B
|
|
| Expression | 戻り値の型 | 要件 |
|---|---|---|
| *p | T&
|
|
| *cp | const T& | *cpと*pは同じオブジェクトを識別します。 |
| p->m | (そのまま) | (*p).mが適切に定義されていれば、(*p).mと同じです。 |
| cp->m | (そのまま) | (*cp).mが適切に定義されていれば、(*cp).mと同じです。 |
| static_cast<A::pointer>(vp) | (そのまま) | static_cast<A::pointer>(vp) == p |
| static_cast<A::const_pointer>(cvp) | (そのまま) | static_cast<A::const_pointer>(cvp) == cp |
| std::pointer_traits<A::pointer>::pointer_to(r) | (そのまま) |
| Expression | 戻り値の型 | 要件 |
|---|---|---|
| a.allocate(n) | A::pointer
|
型T[n]の配列オブジェクトに適したストレージをアロケートし、配列を作成しますが、配列要素は構築しません。例外をスローする場合があります。n == 0の場合、戻り値は未指定です。 |
| a.allocate(n, cvp) (オプション) | a.allocate(n)と同じですが、局所性を高めるためにcvp (nullptrまたはa.allocate()から取得したポインタ) を未指定の方法で使用する場合があります。 | |
| a.allocate_at_least(n) (オプション) (C++23以降) | std::allocation_result <A::pointer> |
型T[cnt]の配列オブジェクトに適したストレージをアロケートし、配列を作成しますが、配列要素は構築しません。その後、{p, cnt}を返します。pはストレージを指し、cntはn以上です。例外をスローする場合があります。 |
| a.deallocate(p, n) | (使用されない) | pが指すストレージをデアロケートします。pは、以前のallocate (またはallocate_at_least(C++23以降)) 呼び出しによって返され、その後のdeallocate呼び出しによって無効化されていない値でなければなりません。nは、以前にallocateに渡された値と一致するか、allocate_at_leastを介した要求と返された要素数の間である必要があります (いずれの境界とも等しくてもよい)。(C++23以降) 例外はスローしません。 |
| a.max_size() (オプション) | A::size_type
|
A::allocate()に渡せる最大値。 |
| a.construct(xp, args...) (オプション) | (使用されない) | xpが指すアドレスにある、以前にアロケートされたストレージに型Xのオブジェクトを構築します。args...をコンストラクタ引数として使用します。 |
| a.destroy(xp) (オプション) | (使用されない) | xpが指す型Xのオブジェクトを破棄しますが、ストレージはデアロケートしません。 |
| Expression | 戻り値の型 | 要件 |
|---|---|---|
| a1 == a2 | bool |
|
| a1 != a2 |
| |
| 宣言 | 効果 | 要件 |
| A a1(a) | a1 == aとなるようにa1をコピー構築します。 (注: すべてのAllocatorはCopyConstructibleも満たします。) |
|
| A a1 = a | ||
| A a(b) | B(a) == bおよびA(b) == aとなるようにaを構築します。 (注: これは、 rebindによって関連付けられたすべてのアロケータが、メモリプールなどの互いのリソースを維持することを意味します。) |
|
| A a1(std::move(a)) | aの以前の値と等しくなるようにa1を構築します。 |
|
| A a1 = std::move(a) | ||
| A a(std::move(b)) | A(b)の以前の値と等しくなるようにaを構築します。 |
|
| 型ID | エイリアス型 | 要件 |
A::is_always_equal(オプション) |
std::true_typeまたはstd::false_type、またはそれらから派生した型。 |
|
| Expression | 戻り値の型 | 説明 |
|---|---|---|
| a.select_on_container_copy_construction() (オプション) |
A
|
|
| 型ID | エイリアス型 | 説明 |
A::propagate_on_container_copy_assignment(オプション) |
std::true_typeまたはstd::false_type、またはそれらから派生した型。 |
|
A::propagate_on_container_move_assignment(オプション) |
| |
A::propagate_on_container_swap(オプション) |
|
注釈
- ↑ 以下のファンシーポインタも参照してください。
- ↑
rebindがオプション (つまりstd::allocator_traitsによって提供される) なのは、このアロケータがSomeAllocator<T, Args>の形式のテンプレートであり、Argsが0個以上の追加のテンプレート型パラメータである場合のみです。
以下を考えます。
- x1とx2、(異なる可能性のある) 型
X::void_pointer、X::const_void_pointer、X::pointer、またはX::const_pointerのオブジェクト。
- その場合、x1とx2は、両方のx1とx2が、これらの4つの型のみを使用するstatic_castのシーケンスを使用して、型
X::const_pointerの対応する2つのオブジェクトpx1とpx2に明示的に変換でき、かつ式px1 == px2がtrueと評価される場合にのみ、等価な値を持つポインタ値です。
以下を考えます。
- w1とw2、型
X::void_pointerのオブジェクト。
- その場合、式w1 == w2とw1 != w2に対して、セマンティクスを変更せずに、両方または一方のオブジェクトを型
X::const_void_pointerの等価な値を持つオブジェクトで置き換えることができます。
以下を考えます。
- p1とp2、型
X::pointerのオブジェクト。
- その場合、式p1 == p2、p1 != p2、p1 < p2、p1 <= p2、p1 >= p2、p1 > p2、p1 - p2に対して、セマンティクスを変更せずに、両方または一方のオブジェクトを型
X::const_pointerの等価な値を持つオブジェクトで置き換えることができます。
上記の要件により、Containerのiteratorとconst_iteratorを比較することが可能になります。
アロケータの完全性要件型
|
(C++17以降) |
[編集] ステートフルおよびステートレスアロケータ
すべてをAllocator型は、ステートフルまたはステートレスのいずれかです。一般に、ステートフルアロケータ型は異なるメモリリソースを示す不均等な値を持つことができますが、ステートレスアロケータ型は単一のメモリリソースを示します。
|
カスタムアロケータがステートレスである必要はありませんが、標準ライブラリでのステートフルアロケータの使用は実装定義です。実装がそのような使用をサポートしない場合、不均等なアロケータ値の使用は、実装定義のランタイムエラーまたは未定義の動作を引き起こす可能性があります。 |
(C++11まで) |
|
カスタムアロケータは状態を含むことができます。各コンテナまたは他のアロケータ認識オブジェクトは、提供されたアロケータのインスタンスを保存し、std::allocator_traitsを介してアロケータの置換を制御します。 |
(C++11以降) |
ステートレスアロケータ型のインスタンスは常に等しく比較されます。ステートレスアロケータ型は通常、空のクラスとして実装され、空基底クラス最適化に適しています。
|
std::allocator_traitsのメンバー型 |
(C++11以降) |
[編集] ファンシーポインタ
メンバー型pointerが生ポインタ型でない場合、それは一般に「ファンシーポインタ」と呼ばれます。このようなポインタは、セグメント化されたメモリアーキテクチャをサポートするために導入され、現在では、生ポインタでアクセスされる均一な仮想アドレス空間とは異なるアドレス空間にアロケートされたオブジェクトにアクセスするために使用されます。ファンシーポインタの例としては、アドレス独立ポインタboost::interprocess::offset_ptrがあり、これによりstd::setのようなノードベースのデータ構造を、すべてのプロセスで異なるアドレスにマップされた共有メモリやメモリマップファイルにアロケートすることが可能になります。ファンシーポインタは、それらを提供したアロケータから独立して使用できます (クラステンプレートstd::pointer_traitsを介して(C++11以降))。関数std::to_addressは、ファンシーポインタから生ポインタを取得するために使用できます。(C++20以降)
|
標準ライブラリにおけるファンシーポインタおよびカスタマイズされたサイズ/異なる型は条件付きでサポートされています。実装によっては、メンバー型 |
(C++11まで) |
コンセプトクエリオブジェクトstd::get_allocatorの定義のために、以下の解説専用コンセプトが定義されます。
解説専用コンセプト/*simple-allocator*/は、Allocator要件の最小限の利用可能性制約を定義します。 |
(C++26以降) |
[編集] 標準ライブラリ
以下の標準ライブラリコンポーネントはAllocator要件を満たします
| デフォルトアロケータ (クラステンプレート) | |
| (C++11) |
多階層コンテナのための多階層アロケータを実装します (クラステンプレート) |
| (C++17) |
構築時に与えられた std::pmr::memory_resource に基づく実行時多態性をサポートするアロケータ (クラステンプレート) |
[編集] 例
C++11アロケータのデモンストレーション (ただし、C++20スタイルに合わせるために[[nodiscard]]が追加されています)。
#include <cstdlib> #include <iostream> #include <limits> #include <new> #include <vector> template<class T> struct Mallocator { typedef T value_type; Mallocator() = default; template<class U> constexpr Mallocator(const Mallocator <U>&) noexcept {} [[nodiscard]] T* allocate(std::size_t n) { if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) throw std::bad_array_new_length(); if (auto p = static_cast<T*>(std::malloc(n * sizeof(T)))) { report(p, n); return p; } throw std::bad_alloc(); } void deallocate(T* p, std::size_t n) noexcept { report(p, n, 0); std::free(p); } private: void report(T* p, std::size_t n, bool alloc = true) const { std::cout << (alloc ? "Alloc: " : "Dealloc: ") << sizeof(T) * n << " bytes at " << std::hex << std::showbase << reinterpret_cast<void*>(p) << std::dec << '\n'; } }; template<class T, class U> bool operator==(const Mallocator <T>&, const Mallocator <U>&) { return true; } template<class T, class U> bool operator!=(const Mallocator <T>&, const Mallocator <U>&) { return false; } int main() { std::vector<int, Mallocator<int>> v(8); v.push_back(42); }
実行結果の例
Alloc: 32 bytes at 0x2020c20 Alloc: 64 bytes at 0x2023c60 Dealloc: 32 bytes at 0x2020c20 Dealloc: 64 bytes at 0x2023c60
[編集] 欠陥レポート
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| LWG 179 | C++98 | pointerとconst_pointerは互いに比較可能である必要がなかった |
必要 |
| LWG 199 | C++98 | a.allocate(0)の戻り値が不明確だった | 未指定である |
| LWG 258 (N2436) |
C++98 | アロケータ間の等価関係は 反射的、対称的、推移的である必要がなかった |
反射的、 対称的、推移的である必要がある |
| LWG 274 | C++98 | Tはconst修飾型または参照型になり得、std::allocatorが不正形式になる可能性があった[1] |
これらの型を禁止 |
| LWG 2016 | C++11 | アロケータのコピー、ムーブ、スワップ操作が 使用時に例外をスローする可能性があった |
例外をスローしない必要がある |
| LWG 2081 | C++98 C++11 |
アロケータはコピー代入 (C++98) およびムーブ代入 (C++11) を サポートする必要がなかった |
必要 |
| LWG 2108 | C++11 | アロケータがステートレスであることを示す方法がなかった | is_always_equalが提供された |
| LWG 2263 | C++11 | LWG issue 179の解決がC++11で誤って削除された そして void_pointerとconst_void_pointerに一般化されていなかった |
復元され、一般化された |
| LWG 2447 | C++11 | Tがvolatile修飾オブジェクト型になり得た |
これらの型を禁止 |
| LWG 2593 | C++11 | アロケータからのムーブがその値を変更する可能性があった | 変更が禁止された |
| P0593R6 | C++98 | allocateはアロケートされたストレージに配列オブジェクトを作成する必要がなかった |
必要 |
- ↑ std::allocatorのメンバー型
referenceとconst_referenceはそれぞれT&とconst T&として定義されています。-
Tが参照型の場合、参照への参照は形成できないため (参照畳み込みはC++11で導入されました)、referenceとconst_referenceは不正形式です。 -
Tがconst修飾されている場合、referenceとconst_referenceは同じになり、address()のオーバーロードセットは不正形式です。
-