名前空間
変種
操作

C++ の名前付き要件: Allocator

From cppreference.com
< cpp‎ | named req
 
 
C++ 名前付き要件
 

オブジェクトへのアクセス/アドレス指定、アロケーション/デアロケーション、構築/破棄の戦略をカプセル化します。

std::stringstd::vector、およびすべてのコンテナ (std::arrayを除く(C++11以降)std::inplace_vectorを除く(C++26以降) から、std::shared_ptr (およびstd::function(C++17まで)) まで、ストレージをアロケートまたは解放する必要があるすべての標準ライブラリコンポーネントは、次の要件を満たすクラス型オブジェクトであるAllocatorを介してこれを行います。

多くのアロケータ要件の実装はオプションです。なぜなら、すべてのAllocatorAwareContainerstd::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のオブジェクト、
  • pstd::allocator_traits<A>::pointer型の値。std::allocator_traits<A>::allocate()を呼び出して取得される、
  • cpstd::allocator_traits<A>::const_pointer型の値。pからの変換によって取得される、
  • vpstd::allocator_traits<A>::void_pointer型の値。pからの変換によって取得される、
  • cvpstd::allocator_traits<A>::const_void_pointer型の値。cpまたはvpからの変換によって取得される、
  • xp、あるcv修飾なしオブジェクト型Xへの逆参照可能なポインタ、
  • r、式*pによって得られる型Tの左辺値、
  • nstd::allocator_traits<A>::size_type型の値。
内部型
型ID エイリアス型 要件
A::pointer (オプション) (未指定)[1]
A::const_pointer (オプション) (未指定)
A::void_pointer (オプション) (未指定)
  • NullablePointerを満たします。
  • A::pointerA::void_pointerに変換可能です。
  • B::void_pointerA::void_pointerは同じ型です。
A::const_void_pointer (オプション) (未指定)
  • NullablePointerを満たします。
  • A::pointerA::const_pointer、およびA::void_pointerA::const_void_pointerに変換可能です。
  • B::const_void_pointerA::const_void_pointerは同じ型です。
A::value_type T
A::size_type (オプション) (未指定)
  • 符号なし整数型。
  • Aがアロケートできる最大のオブジェクトのサイズを表すことができます。
A::difference_type (オプション) (未指定)
  • 符号付き整数型。
  • Aによってアロケートされたオブジェクトへの任意の2つのポインタの差を表すことができます。
A::template rebind<U>::other
(オプション)[2]
B
  • 任意のUに対して、B::template rebind<T>::otherAです。
ポインタに対する操作
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はストレージを指し、cntn以上です。例外をスローする場合があります。
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を介してデアロケートできる場合にのみtrueです。
  • 反射的、対称的、推移的な関係を確立します。
  • 例外をスローしません。
a1 != a2
  • !(a1 == a2)と同じです。
宣言 効果 要件
A a1(a) a1 == aとなるようにa1をコピー構築します。
(注: すべてのAllocatorCopyConstructibleも満たします。)
  • 例外をスローしません。
A a1 = a
A a(b) B(a) == bおよびA(b) == aとなるようにaを構築します。
(注: これは、rebindによって関連付けられたすべてのアロケータが、メモリプールなどの互いのリソースを維持することを意味します。)
  • 例外をスローしません。
A a1(std::move(a)) aの以前の値と等しくなるようにa1を構築します。
  • 例外をスローしません。
  • aの値は変更されず、a1 == aです。
A a1 = std::move(a)
A a(std::move(b)) A(b)の以前の値と等しくなるようにaを構築します。
  • 例外をスローしません。
型ID エイリアス型 要件
A::is_always_equal
(オプション)
std::true_typeまたはstd::false_type、またはそれらから派生した型。
  • Aの任意のアロケータが常に等しいと比較される場合にtrue
  • (提供されない場合、std::allocator_traitsはこれをstd::is_empty<A>::typeにデフォルト設定します。)
コンテナ操作への影響
Expression 戻り値の型 説明
a.select_on_container_copy_construction()
(オプション)
A
  • aを現在使用しているコンテナからコピー構築されるコンテナが使用するAのインスタンスを提供します。
  • (通常、aのコピーまたはデフォルト構築されたAのいずれかを返します。)
型ID エイリアス型 説明
A::propagate_on_container_copy_assignment
(オプション)
std::true_typeまたはstd::false_type、またはそれらから派生した型。
  • std::true_typeまたはそれから派生した型。型Aのアロケータが、それを使用するコンテナがコピー代入されるときにコピーされる必要がある場合。
  • このメンバーがstd::true_typeであるかそれから派生している場合、ACopyAssignableを満たし、コピー操作は例外をスローしてはなりません。
  • ソースとターゲットコンテナのアロケータが等しくないと比較される場合、コピー代入は、要素 (およびアロケータ) をコピーする前に、古いアロケータを使用してターゲットのメモリをデアロケートし、新しいアロケータを使用してそれをアロケートしなければならないことに注意してください。
A::propagate_on_container_move_assignment
(オプション)
  • std::true_typeまたはそれから派生した型。型Aのアロケータが、それを使用するコンテナがムーブ代入されるときにムーブされる必要がある場合。
  • このメンバーがstd::true_typeであるかそれから派生している場合、AMoveAssignableを満たし、ムーブ操作は例外をスローしてはなりません。
  • このメンバーが提供されないかstd::false_typeから派生しており、ソースとターゲットコンテナのアロケータが等しくないと比較される場合、ムーブ代入はソースメモリの所有権を取得できず、必要に応じて自身のメモリのサイズを変更しながら、要素を個別にムーブ代入またはムーブ構築しなければなりません。
A::propagate_on_container_swap
(オプション)
  • std::true_typeまたはそれから派生した型。型Aのアロケータが、それらを使用する2つのコンテナがスワップされるときにスワップされる必要がある場合。
  • このメンバーがstd::true_typeであるかそれから派生している場合、型ASwappableを満たし、スワップ操作は例外をスローしてはなりません。
  • このメンバーが提供されないかstd::false_typeから派生しており、2つのコンテナのアロケータが等しくないと比較される場合、コンテナのスワップの動作は未定義です。

注釈

  1. 以下のファンシーポインタも参照してください。
  2. rebindがオプション (つまりstd::allocator_traitsによって提供される) なのは、このアロケータがSomeAllocator<T, Args>の形式のテンプレートであり、Argsが0個以上の追加のテンプレート型パラメータである場合のみです。

以下を考えます。

  • x1x2、(異なる可能性のある) 型X::void_pointerX::const_void_pointerX::pointer、またはX::const_pointerのオブジェクト。
その場合、x1x2は、両方のx1x2が、これらの4つの型のみを使用するstatic_castのシーケンスを使用して、型X::const_pointerの対応する2つのオブジェクトpx1px2に明示的に変換でき、かつ式px1 == px2trueと評価される場合にのみ、等価な値を持つポインタ値です。

以下を考えます。

  • w1w2、型X::void_pointerのオブジェクト。
その場合、式w1 == w2w1 != w2に対して、セマンティクスを変更せずに、両方または一方のオブジェクトを型X::const_void_pointer等価な値を持つオブジェクトで置き換えることができます。

以下を考えます。

  • p1p2、型X::pointerのオブジェクト。
その場合、式p1 == p2p1 != p2p1 < p2p1 <= p2p1 >= p2p1 > p2p1 - p2に対して、セマンティクスを変更せずに、両方または一方のオブジェクトを型X::const_pointer等価な値を持つオブジェクトで置き換えることができます。

上記の要件により、Containeriteratorconst_iteratorを比較することが可能になります。

アロケータの完全性要件

Tに対するアロケータ型Xは、Tが完全型であるかどうかに関わらず、次の両方が真である場合、アロケータの完全性要件をさらに満たします。

  • Xは完全型です。
  • value_typeを除き、std::allocator_traits<X>のすべてのメンバー型は完全型です。
(C++17以降)

[編集] ステートフルおよびステートレスアロケータ

すべてをAllocator型は、ステートフルまたはステートレスのいずれかです。一般に、ステートフルアロケータ型は異なるメモリリソースを示す不均等な値を持つことができますが、ステートレスアロケータ型は単一のメモリリソースを示します。

カスタムアロケータがステートレスである必要はありませんが、標準ライブラリでのステートフルアロケータの使用は実装定義です。実装がそのような使用をサポートしない場合、不均等なアロケータ値の使用は、実装定義のランタイムエラーまたは未定義の動作を引き起こす可能性があります。

(C++11まで)

カスタムアロケータは状態を含むことができます。各コンテナまたは他のアロケータ認識オブジェクトは、提供されたアロケータのインスタンスを保存し、std::allocator_traitsを介してアロケータの置換を制御します。

(C++11以降)

ステートレスアロケータ型のインスタンスは常に等しく比較されます。ステートレスアロケータ型は通常、空のクラスとして実装され、空基底クラス最適化に適しています。

std::allocator_traitsのメンバー型is_always_equalは、アロケータ型がステートレスであるかどうかを判断するために意図的に使用されます。

(C++11以降)

[編集] ファンシーポインタ

メンバー型pointerが生ポインタ型でない場合、それは一般に「ファンシーポインタ」と呼ばれます。このようなポインタは、セグメント化されたメモリアーキテクチャをサポートするために導入され、現在では、生ポインタでアクセスされる均一な仮想アドレス空間とは異なるアドレス空間にアロケートされたオブジェクトにアクセスするために使用されます。ファンシーポインタの例としては、アドレス独立ポインタboost::interprocess::offset_ptrがあり、これによりstd::setのようなノードベースのデータ構造を、すべてのプロセスで異なるアドレスにマップされた共有メモリやメモリマップファイルにアロケートすることが可能になります。ファンシーポインタは、それらを提供したアロケータから独立して使用できます (クラステンプレートstd::pointer_traitsを介して(C++11以降))。関数std::to_addressは、ファンシーポインタから生ポインタを取得するために使用できます。(C++20以降)

標準ライブラリにおけるファンシーポインタおよびカスタマイズされたサイズ/異なる型は条件付きでサポートされています。実装によっては、メンバー型pointerconst_pointersize_type、およびdifference_typeがそれぞれvalue_type*const value_type*std::size_t、およびstd::ptrdiff_tである必要がある場合があります。

(C++11まで)

コンセプト

クエリオブジェクトstd::get_allocatorの定義のために、以下の解説専用コンセプトが定義されます。

template<class Alloc>

concept /*simple-allocator*/ = requires(Alloc alloc, std::size_t n)
{
    { *alloc.allocate(n) } -> std::same_as<typename Alloc::value_type&>;
    { alloc.deallocate(alloc.allocate(n), n) };  
} && std::copy_constructible<Alloc>

  && std::equality_comparable<Alloc>;

解説専用コンセプト/*simple-allocator*/は、Allocator要件の最小限の利用可能性制約を定義します。

(C++26以降)

[編集] 標準ライブラリ

以下の標準ライブラリコンポーネントはAllocator要件を満たします

デフォルトアロケータ
(クラステンプレート) [編集]
多階層コンテナのための多階層アロケータを実装します
(クラステンプレート) [編集]
構築時に与えられた 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 pointerconst_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_pointerconst_void_pointerに一般化されていなかった
復元され、一般化された
LWG 2447 C++11 Tがvolatile修飾オブジェクト型になり得た これらの型を禁止
LWG 2593 C++11 アロケータからのムーブがその値を変更する可能性があった 変更が禁止された
P0593R6 C++98 allocate
アロケートされたストレージに配列オブジェクトを作成する必要がなかった
必要
  1. std::allocatorのメンバー型referenceconst_referenceはそれぞれT&const T&として定義されています。
    • Tが参照型の場合、参照への参照は形成できないため (参照畳み込みはC++11で導入されました)、referenceconst_referenceは不正形式です。
    • Tがconst修飾されている場合、referenceconst_referenceは同じになり、address()のオーバーロードセットは不正形式です。
English 日本語 中文(简体) 中文(繁體)