Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C++17の新機能 アロケータ編 / new features of C++17 - allocator

C++17の新機能 アロケータ編 / new features of C++17 - allocator

C++17 で追加された多相アロケータの紹介(アロケータのおさらい付き)です。
アロケータ編と言いながら C++17 で追加された is_always_equal に触れられていないのは、単なる時間切れです…

Miutsuru kariya

October 04, 2018
Tweet

More Decks by Miutsuru kariya

Other Decks in Programming

Transcript

  1. C++17 の新機能
    アロケータ編
    2018/10/4 鳥頭かりやマン
    1

    View Slide

  2. アロケータおいしい!!!
    2
    注:個人の感想です

    View Slide

  3. 多相アロケータ
    3

    View Slide

  4. 多相アロケータ
    4
    N1850 Towards a Better Allocator Model
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1850.pdf
    N3525 Polymorphic Allocators
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3525.pdf
    N3726 Polymorphic Memory Resources
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3726.pdf
    N3816 Polymorphic Memory Resources - r1
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3816.pdf
    N3916 Polymorphic Memory Resources - r2
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3916.pdf
    2005 年からってお前導入に10年以上かかってんのかよ…

    View Slide

  5. 多相アロケータ
    5
    導入の背景

    View Slide

  6. 多相アロケータ
    導入の背景
    6
    コンテナの型にアロケータの
    型が現れるのは控えめに
    言って頭おかしい
    [email protected]++11 by Cryolite 先生 参照

    View Slide

  7. 多相アロケータ
    導入の背景
    7
    // このコンテナの要素を…
    std::vector v1 = { 1, 2, 3 };
    // このコンテナにコピーしたい…
    std::vector> v2 = v1;

    View Slide

  8. 多相アロケータ
    導入の背景
    8
    // このコンテナの要素を…
    std::vector v1 = { 1, 2, 3 };
    // このコンテナにコピーしたい…けど出来ない…
    std::vector> v2 = v1;

    View Slide

  9. 多相アロケータ
    導入の背景
    9
    // このコンテナの要素を…
    std::vector v1 = { 1, 2, 3 };
    // コピーするのめんどくさっ!
    std::vector>
    v2{v1.begin(), v1.end()};

    View Slide

  10. 多相アロケータ
    導入の背景
    10
    // このコンテナのデータを…
    std::vector> v;
    // vector の引数を取る関数に…
    int calc(const std::vector&);
    // 渡したい…
    auto dt = calc(v);

    View Slide

  11. 多相アロケータ
    導入の背景
    11
    // このコンテナのデータを…
    std::vector> v;
    // vector の引数を取る関数に…
    int calc(const std::vector&);
    // 渡したい…けど出来ない…
    auto dt = calc(v);

    View Slide

  12. 多相アロケータ
    導入の背景
    12
    // このコンテナのデータを…
    std::vector> v;
    // vector の引数を取る関数に…
    int calc(const std::vector&);
    // コピーしてから渡すのマジかよ…
    std::vector v2{v.begin(), v.end()};
    auto dt = calc(v2);

    View Slide

  13. 多相アロケータ
    導入の背景
    13
    // このコンテナのデータを…
    std::vector> v;
    // 関数が弄れるならテンプレートにすれば…
    template typename A>
    int calc(const std::vector>&);
    // 渡せるけど、わざわざテンプレート書くのうぜぇ…
    auto dt = calc(v);

    View Slide

  14. 多相アロケータ
    導入の背景
    14
    // この文字列と…
    std::string s;
    // この文字列を…
    std::basic_string,
    my_allocator> t;
    // 比較したい…
    auto result = s == t;

    View Slide

  15. 多相アロケータ
    導入の背景
    15
    // この文字列と…
    std::string s;
    // この文字列を…
    std::basic_string,
    my_allocator> t;
    // 比較したい…けどできない…
    auto result = s == t;

    View Slide

  16. 多相アロケータ
    導入の背景
    16
    // この文字列と…
    std::string s;
    // この文字列を…
    std::basic_string,
    my_allocator> t;
    // 比較したい…まぁ許容範囲かな…
    auto result = s == std::string_view(t);

    View Slide

  17. 多相アロケータ
    導入の背景
    17
    // この文字列と…
    std::string s;
    // この文字列を… てかクラス名なげぇよ…
    std::basic_string,
    my_allocator> t;
    // 比較したい…まぁ許容範囲かな…
    auto result = s == std::string_view(t);

    View Slide

  18. 多相アロケータ
    導入の背景
    18
    // この文字列と…
    std::string s;
    // この文字列を… てかクラス名なげぇよ…
    std::basic_string,
    my_allocator> t;
    // 比較したい…てか比較にアロケータ関係無いの
    にこれ出来ないのどう考えても規格のバグやろ…
    auto result = s == t;

    View Slide

  19. 多相アロケータ
    19
    対応

    View Slide

  20. 多相アロケータ
    対応
    20
    標準アロケータ
    (std::allocator)魔改造
    してメモリ割当戦略の
    違いが型に出ないよう
    にしよう!!!

    View Slide

  21. 多相アロケータ
    対応
    21
    標準アロケータ
    (std::allocator)魔改造
    してメモリ割当戦略の
    違いが型に出ないよう
    にしよう!!!

    View Slide

  22. 多相アロケータ
    対応
    22
    チッ
    メモリ割当戦略の違い
    が型に出ない新しいア
    ロケータ作ろう!!!

    View Slide

  23. 多相アロケータ
    対応
    23
    どうやって?

    View Slide

  24. 多相アロケータ
    対応
    24
    多相アロケータ
    template
    std::pmr::polymorphic_allocator
    pmr ⇒ Polymorphic Memory Resource
    polymorphic ⇒ 多相
    allocator ⇒アロケータ

    View Slide

  25. 多相アロケータ
    対応
    25
    多相?

    View Slide

  26. 多相アロケータ
    対応
    26
    polymorphic_allocator
    自身ではメモリ割当、
    解放処理を実装せず、
    std::pmr::memory_resource
    (の具象派生クラス)にメモリ
    割当、解放処理を委譲する。

    View Slide

  27. 多相アロケータ
    対応
    27
    古き良き
    Strategy Pattern !!!
    つまり動的多相
    GoF万歳!!!1!

    View Slide

  28. 多相アロケータ
    対応
    28
    あ、あと、標準アロケータを多相にする
    夢が破れて、標準コンテナを多相アロ
    ケータで使うのが面倒になっちゃったん
    で、標準コンテナに多相アロケータを使
    うエイリアステンプレートを追加しよう!

    View Slide

  29. 多相アロケータ
    対応
    29
    こんなやつ
    std::pmr::vector
    std::pmr::map

    全部あるよ
    (もちろん std::array 以外)

    View Slide

  30. 多相アロケータ
    対応
    30
    定義はこんな感じ
    namespace std::pmr {
    template typename C = less>
    using map = std::mappolymorphic_allocator>>
    }
    要は、アロケータの型だけ固定化している。
    テンプレートのデフォルトを変更しただけじゃないので注意。
    (std::pmr::vector> とはできない)
    アロケータ用の
    テンプレート
    引数は無い

    View Slide

  31. 多相アロケータ
    31
    polymorphic_allocator や
    memory_resource の詳細に行く
    前に、アロケータのおさらい

    View Slide

  32. 多相アロケータ
    アロケータおさらい
    32
    そもそもアロケータとは?

    View Slide

  33. 多相アロケータ
    アロケータおさらい
    33
    そもそもアロケータとは?

    メモリ割当をカスタマイズ
    するためのもの

    View Slide

  34. 多相アロケータ
    アロケータおさらい
    34
    そもそもアロケータとは?

    メモリ割当をカスタマイズ
    するためのもの
    だけじゃない!!!
    [email protected]++11 by Cryolite 先生 参照

    View Slide

  35. 多相アロケータ
    アロケータおさらい
    35
    アロケータが無い場合の
    動的メモリ使用方法

    new 式
    delete 式

    View Slide

  36. 多相アロケータ
    アロケータおさらい
    36
    new式

    // メモリを割り当てて…
    void* addr = ::operator new(sizeof(T));
    // ホントは T::operator new もあるかもだけど…
    // コンストラクタを呼び出す
    T* p = ::new(addr) T(args...);

    View Slide

  37. 多相アロケータ
    アロケータおさらい
    37
    A 型のアロケータ a

    // メモリを割り当てて…
    A::pointer p = a.allocate(1);
    // ホントは std::allocator_traits::pointer std::allocator_traits::allocate(a, 1)
    // コンストラクタを呼び出す
    a.construct(std::addressof(*p), args...);
    // ホントは std::allocator_traits::construct(a, std::addressof(*p), args...)

    View Slide

  38. 多相アロケータ
    アロケータおさらい
    38
    delete式

    // デストラクタを呼び出して…
    p->~T();
    // メモリを解放する
    ::operator delete(addr);
    // ホントは T::operator delete もあるかもだけど…

    View Slide

  39. 多相アロケータ
    アロケータおさらい
    39
    A 型のアロケータ a

    // デストラクタを呼び出して…
    a.destroy(std::addressof(*p));
    // ホントは std::allocator_traits::destroy(a, std::addressof(*p))
    // メモリを解放する
    a.deallocate(p, 1);
    // ホントは std::allocator_traits::deallocate(a, p, 1)

    View Slide

  40. 多相アロケータ
    アロケータおさらい
    40
    アロケータのポイント①
    メモリ割当・解放とコンストラクタ・デスト
    ラクタ呼び出しが完全に分離している
    コンテナの要素型とアロケーションした
    いオブジェクトの型が必ずしも一致しな
    いので(むしろ一致する方が稀)

    View Slide

  41. 多相アロケータ
    アロケータおさらい
    41
    std::vectorの場合
    T T T T T T T T T T
    T型配列が割り当てられる。
    こんなコンテナはむしろ稀。
    std::vector
    ポインタ サイズ 容量 アロケータ

    View Slide

  42. 多相アロケータ
    アロケータおさらい
    42
    std::forward_listの場合
    ポインタ

    ノード型(node的な)のメモリが割り当てられる。
    ほとんどのコンテナがこれ。
    forward_listにしたのはポインタ書くのが面倒だったから…
    std::forward_list
    ポインタ サイズ アロケータ
    ポインタ

    ポインタ

    View Slide

  43. 多相アロケータ
    アロケータおさらい
    43
    でも、指定されたアロケータは
    T型配列しかアロケーション
    できない…

    View Slide

  44. 多相アロケータ
    アロケータおさらい
    44
    でも、指定されたアロケータは
    T型配列しかアロケーション
    できない…

    rebind

    View Slide

  45. 多相アロケータ
    アロケータおさらい
    45
    アロケータの rebind

    要素型が T 型のアロケータ型 A から、
    要素型が U 型のアロケータ型 B を生成
    (std::allocator ⇒ std::allocator みたいな感じ)
    using B = typename A::rebind::other;
    ホントは using B = std::allocator_traits::rebind_alloc

    View Slide

  46. 多相アロケータ
    アロケータおさらい
    46
    rebind を使えば、渡されたアロケータから
    任意の型用のアロケータを生成可能
    // 型T 用のアロケータから型U用のアロケータを生成
    B b(a);
    ただし、常に以下が成り立つ必要がある
    assert(a == A(b));

    View Slide

  47. 多相アロケータ
    アロケータおさらい
    47
    アロケータの operator== は、一方で割り当てた
    メモリがもう一方で解放できる場合にのみ
    true を返さなきゃならない

    a == A(b) じゃなきゃならない

    a で割り当てられたメモリは
    A(b) でも解放できなきゃならない

    View Slide

  48. 多相アロケータ
    アロケータおさらい
    48
    C++11 から、アロケータは
    オブジェクト毎に状態を
    持てるようになった。
    [email protected]++11 by Cryolite 先生 参照
    しかし、rebind したアロケータは
    状態を共有しなければならない
    状態を共有しないとメモリ解放無理でしょ…

    View Slide

  49. 多相アロケータ
    アロケータおさらい
    49
    つまりこんな感じ
    これ割と重要な要件です…
    でもこれに触れてる日本語の記事を見かけない…
    T 型用
    アロケータ
    a
    U 型用
    アロケータ
    b
    a と b と A(b)に
    共有の状態
    rebind rebind T 型用
    アロケータ
    A(b)

    View Slide

  50. 多相アロケータ
    アロケータおさらい
    50
    ちなみに、実際は rebind だけじゃ
    なくて、普通のコピーやムーブでも
    状態を共有しなくちゃならない。
    アロケータって引数としてコピーされたりするから当たり前っちゃあ当たり前なんだけど…
    しかも、ムーブされた元も状態を
    保持し続けなきゃならない。
    アロケータにはムーブなんてなかったんや…

    View Slide

  51. 多相アロケータ
    アロケータおさらい
    51
    多相アロケータは型に
    メモリ割当戦略が現れない

    View Slide

  52. 多相アロケータ
    アロケータおさらい
    52
    多相アロケータは型に
    メモリ割当戦略が現れない

    多相アロケータはオブジェクト毎に
    メモリ割当戦略が異なる

    View Slide

  53. 多相アロケータ
    アロケータおさらい
    53
    多相アロケータは型に
    メモリ割当戦略が現れない

    多相アロケータはオブジェクト毎に
    メモリ割当戦略が異なる

    多相アロケータはオブジェクト毎に
    状態を持つ

    View Slide

  54. 多相アロケータ
    アロケータおさらい
    54
    多相アロケータは
    strategy pattern になっているので、
    strategy オブジェクト(memory_resource)側に
    メモリ割当に関する状態が持たれている。
    多相アロケータ本体は、memory_resource
    へのポインタを持っているだけ。
    さすがにちゃんと考えられている…
    てかそのために strategy pattern にしたのかな、教えてエロい人…

    View Slide

  55. 多相アロケータ
    アロケータおさらい
    55
    アロケータのポイント②
    メモリ割当・解放だけじゃなくて、
    コンストラクタ、デストラクタ呼び出しも
    カスタマイズ可能
    a.construct(std::addressof(*p), args...);
    a.destroy(std::addressof(*p));

    View Slide

  56. 多相アロケータ
    アロケータおさらい
    56
    素朴な疑問
    コンストラクタ、デストラクタ呼び出しを
    カスタマイズできるのは分かったけど、
    そもそも、コンストラクタ、デストラクタ
    呼び出しなんてカスタマイズしたい?

    View Slide

  57. 多相アロケータ
    アロケータおさらい
    57
    答え
    アロケータをコンテナの要素に
    伝搬したい時に、コンストラクタ
    呼び出しのカスタマイズが必要
    それ以外のケースやデストラクタ呼び出しのカスタマイズって必要な時ありますかね?
    教えてエロい人!

    View Slide

  58. 多相アロケータ
    アロケータおさらい
    58
    答え
    アロケータをコンテナの要素に
    伝搬したい時に、コンストラクタ
    呼び出しのカスタマイズが必要
    それ以外のケースやデストラクタ呼び出しのカスタマイズって必要な時ありますかね?
    教えてエロい人!
    勉強会後追記
    デバッグログとかはありますね…

    View Slide

  59. 多相アロケータ
    アロケータおさらい
    59
    string の vector の場合のイメージ(普通バージョン)
    絵心が無さ過ぎてcpprefjp から拝借…
    stringは、コンテナの
    アロケータオブジェク
    トとは異なる、自分独
    自のアロケータオブ
    ジェクトを持っている。

    View Slide

  60. 多相アロケータ
    アロケータおさらい
    60
    string の vector の場合のイメージ(伝搬バージョン)
    絵心が無さ過ぎてcpprefjp から拝借…
    stringはアロケータオ
    ブジェクトを持ってい
    るものの、コンテナの
    アロケータオブジェク
    トと状態を共有してい
    る。

    View Slide

  61. 多相アロケータ
    アロケータおさらい
    61
    素朴な疑問その2
    そんなのコンテナに要素格納する時に、
    コンテナのアロケータオブジェクトを
    取得して、コンストラクタに明示的に
    指定してやれば済む話しじゃないの?

    View Slide

  62. 多相アロケータ
    アロケータおさらい
    62
    答えその2
    それはそうなんだけど、
    毎回必ず手動でやるのは面倒だし
    間違いも起きやすい。

    View Slide

  63. 多相アロケータ
    アロケータおさらい
    63
    手動で指定する例
    // カスタムアロケータ MyAlloc のオブジェクト a
    MyAlloc a;
    // カスタムアロケータ MyAlloc を使った文字列型 MyStr
    using MyStr = std::basic_string, MyAlloc>;
    // MyStr を格納する、アロケータオブジェクト a を使ったvector
    std::vector> v(a);
    // アロケータオブジェクト a を使った要素格納
    v.emplace_back("first element", a);
    v.emplace_back("second element", a);

    View Slide

  64. 多相アロケータ
    アロケータおさらい
    64
    自動だった場合の例
    // カスタムアロケータ MyAlloc のオブジェクト a
    MyAlloc a;
    // カスタムアロケータ MyAlloc を使った文字列型 MyStr
    using MyStr = std::basic_string, MyAlloc>;
    // MyStr を格納する、アロケータオブジェクト a を使ったvector
    std::vector> v(a);
    // アロケータオブジェクト a を使った要素格納
    v.emplace_back("first element");
    v.emplace_back("second element");

    View Slide

  65. 多相アロケータ
    アロケータおさらい
    65
    ほんのちょっとの違いに見えるけど、
    コードが離れていたり、ネストが深かっ
    たり(string の vector の map みたいな)
    すると、かなり厳しい…

    View Slide

  66. 多相アロケータ
    アロケータおさらい
    66
    そこで、アロケータの
    construct メンバ関数に
    小細工する

    View Slide

  67. 多相アロケータ
    アロケータおさらい
    67
    普通の場合
    a.construct(addr, args);

    ::new((void*)addr) T(std::forward(args)...);
    至って普通の配置new…

    View Slide

  68. 多相アロケータ
    アロケータおさらい
    68
    要素に伝搬させたいアロケータの場合
    a.construct(addr, args);

    ::new((void*)addr) T(std::allocator_arg, a, std::forward(args)...);
    または
    ::new((void*)addr) T(std::forward(args)..., a);
    コンストラクタ引数の最初、あるいは最後に自分自身を紛れ込ませる
    (利用者はアロケータは明示的には指定しない)
    ちなみに、std::allocator_arg は引数の先頭がアロケータであることを示す
    タグ std::allocator_arg_t 型のダミーオブジェクト

    View Slide

  69. 多相アロケータ
    アロケータおさらい
    69
    ただし、アロケータが自分を要素に伝搬させたい場合でも
    ① そもそも要素がアロケータを使う必要が無い(int とか)
    ② アロケータは使用するけど型が違っている
    ③ コンストラクタでアロケータを指定できない
    などの場合があるので、その場合は普通に構築したい。
    そこで、std::uses_allocator と言う型トレイツで
    伝搬させるかどうかを制御できるようにする。

    View Slide

  70. 多相アロケータ
    アロケータおさらい
    70
    std::uses_allocator
    T 型のオブジェクトが Alloc 型のアロケータを使用した構築を
    サポートしているかどうかを判別する型トレイツ。
    デフォルトでは T::allocator_type と言うメンバ型が定義されて
    いて、かつ std::is_convertible_v が
    true であれば、std::true_type から派生、さもなければ
    std::false_type から派生。
    T::allocator_type と言うメンバ型が定義されていなくても T が
    Alloc 型のアロケータを使用した構築をサポートしているので
    あれば std::true_type から派生するように特殊化しておく。

    View Slide

  71. 多相アロケータ
    アロケータおさらい
    71
    uses-allocator 構築
    obj を構築対象の T 型のオブジェクト、v1, v2, …, vN をそれぞれ V1, V2, …, VN 型
    のコンストラクタ引数、alloc を Alloc 型のアロケータとすると…
    • std::uses_allocator_v が false で、かつ std::is_constructible_vV2, ..., VN> が true なら、obj(v1, v2, ..., vN) として構築
    • そうでなくて、std::uses_allocator_v が true で、かつ
    std::is_constructible_v が true な
    ら、obj(std::allocator_arg, alloc, v1, v2, ..., vN) として構築
    • そうでなくて、std::uses_allocator_v が true で、かつ
    std::is_constructible_v が true なら、obj(v1, v2, ..., vN,
    alloc) として構築
    • 上記のいずれでもない場合、ill-formed

    View Slide

  72. 多相アロケータ
    アロケータおさらい
    72
    uses-allocator 構築をサポートするアロケータ
    • std::polymorphic_allocator
    • std::scoped_allocator_adaptor
    ただし、scoped_allocator_adaptor 自体は本物のアロケータではないので
    (名前の通り、アダプタ)、実際の処理は uses-allocator 構築とはちょっと違う。
    あと、scoped_allocator_adaptor と polymorphic_allocator は組み合わせては使えないと思う(多分…)

    View Slide

  73. 多相アロケータ
    アロケータおさらい
    73
    uses-allocator 構築をサポートしている型
    • std::array 以外の標準コンテナ
    • 標準コンテナアダプタ(std::stackとか)
    • std::basic_string
    • std::tuple(自分はアロケータ使わないけど※1)
    • std::match_results(正規表現の検索結果、一部※2)
    • std::promise(スレッド系、一部※2)
    ※1 全ての要素型に対して、uses-allocator 構築を行う
    ※2 引数がアロケータだけのコンストラクタのみ存在

    View Slide

  74. 多相アロケータ
    アロケータおさらい
    74
    tuple があるのに何で pair が無いの?

    View Slide

  75. 多相アロケータ
    アロケータおさらい
    75
    tuple があるのに何で pair が無いの?

    コンストラクタが多すぎると言う理由で、pair の
    コンストラクタからアロケータを使用するヤツが
    削除されてしまった。

    View Slide

  76. 多相アロケータ
    アロケータおさらい
    76
    tuple があるのに何で pair が無いの?

    コンストラクタが多すぎると言う理由で、pair の
    コンストラクタからアロケータを使用するヤツが
    削除されてしまった。

    pair の各要素を uses-allocator 構築するために
    は、各アロケータで特別対応する必要がある
    polymorphic_allocator と scoped_allocator_adaptor は特別対応している

    View Slide

  77. 多相アロケータ
    アロケータおさらい
    77
    勉強会での光成さん(@herumi)からの質問
    自作アロケータで uses-allocator 構築する場合、
    pair に特別対応しなかったらコンテナの要素に
    pair 使うとエラーになる?

    ならないです。ただし、コンテナのアロケータは
    pair の各要素には伝搬しなくなります。
    勉強会での
    話を基に追加

    View Slide

  78. 多相アロケータ
    アロケータおさらい
    78
    残念なお知らせ
    C++17 で追加された超有能三兄弟、
    std::optional、std::variant、std::any は
    uses-allocator 構築をサポートしていない。
    したがって、コンテナの要素がこの兄弟の場合、
    コンテナのアロケータはこの兄弟の要素には
    伝搬しない。
    わりと悲しい…
    当日スライドに入れ
    忘れてて勉強会で
    口頭補足したやつ

    View Slide

  79. 多相アロケータ
    アロケータおさらい
    79
    余談
    • std::shared_ptr はアロケータを使用するが、uses-
    allocator 構築はサポートしない。
    • std::function、std::packaged_task は、C++14 までは
    uses-allocator 構築をサポートしていることになって
    いたが、いろいろ問題があったらしく、C++17 からそ
    もそもアロケータ自体をサポートしなくなった。

    View Slide

  80. 多相アロケータ
    アロケータおさらい
    80
    アロケータのポイント➂
    コピー構築、コピー代入、ムーブ代入、
    スワップ時のアロケータ伝搬を
    制御可能

    View Slide

  81. 多相アロケータ
    アロケータおさらい
    81
    uses-allocator 構築では、construct メンバ関数で
    コンテナの要素に対するアロケータ伝搬を制御
    したが、コピー構築、コピー代入、ムーブ代入、
    スワップ時のアロケータ伝搬もメンバの定義に
    よって制御できるようになっている。
    ちなみに、ムーブ構築の場合は問答無用で伝搬する

    View Slide

  82. 多相アロケータ
    アロケータおさらい
    82
    コピー構築時の制御
    コピー構築時には、コピー元アロケータの以
    下のメンバ関数を呼び出してアロケータオブ
    ジェクトを取得する。
    現在 API 名最長選手権 C++ 標準ライブラリ 同率 6 位※
    Alloc select_on_container_copy_construction() const;
    ※ API名最長選手権 in C++ by yohhoy 先生 参照

    View Slide

  83. 多相アロケータ
    アロケータおさらい
    83
    コピー代入時の制御
    コピー代入時には、以下のメンバ型が
    true_type だった場合にはコピー元アロケータ
    を、false_type(デフォルト)だった場合にはデ
    フォルト構築されたアロケータを使用する。
    現在 API 名最長選手権 C++ 標準ライブラリ 同率 3 位※
    propagate_on_container_copy_assignment
    ※ API名最長選手権 in C++ by yohhoy 先生 参照

    View Slide

  84. 多相アロケータ
    アロケータおさらい
    84
    ムーブ代入時の制御
    ムーブ代入時には、以下のメンバ型が
    true_type だった場合にはムーブ元アロケー
    タを、false_type(デフォルト)だった場合には
    デフォルト構築されたアロケータを使用する。
    現在 API 名最長選手権 C++ 標準ライブラリ 同率 3 位※
    propagate_on_container_move_assignment
    ※ API名最長選手権 in C++ by yohhoy 先生 参照

    View Slide

  85. 多相アロケータ
    アロケータおさらい
    85
    スワップ時の制御
    スワップ時には、以下のメンバ型が
    true_type だった場合にはアロケータもスワッ
    プし、false_type(デフォルト)だった場合には
    アロケータはスワップしない。
    propagate_on_container_swap

    View Slide

  86. 多相アロケータ
    アロケータおさらい
    86
    勉強会での光成さん(@herumi)からの質問
    スワップ時にアロケータをスワップしない
    選択肢ってあるの?

    無いです。もしあるとすると、もともと同じアロケータ
    だった場合。アロケータが違うのにスワップしない
    場合は、みんな大好き未定義動作になります。
    つまり、その場合コンテナのスワップはやっちゃダメ、
    ということです。
    勉強会での
    話を基に追加

    View Slide

  87. 多相アロケータ
    アロケータおさらい
    87
    ちなみに、多相アロケータは
    select_on_container_copy_construction は
    自分のコピーじゃなく新たなデフォルト構築された
    アロケータを返し、propagate_on_container_* は
    全部 false_type です。
    つまり、コピー構築、コピー・ムーブ代入、スワップ
    ではアロケータ絶対伝搬させないマンです。
    このあたりは、提案者の哲学(アロケータは構築時に
    一旦設定したら変更されるべきではない)が
    反映されているのではないかと思います。
    勉強会での
    話を基に追加

    View Slide

  88. 多相アロケータ
    アロケータおさらい
    88
    アロケータのポイント④
    メモリ割当・解放の際の「ポインタ」は
    T* じゃなくてもよい。
    A::pointer p = a.allocate(1);
    a.deallocate(p, 1);

    View Slide

  89. 多相アロケータ
    アロケータおさらい
    89
    アロケータのポイント④
    ただし、コンストラクタ、デストラクタ
    呼び出しの際は普通のポインタ
    a.construct(std::addressof(*p), args...);
    a.destroy(std::addressof(*p));

    View Slide

  90. 多相アロケータ
    アロケータおさらい
    90
    T* じゃない「ポインタ」は
    「ファンシーポインタ」と呼ばれる。
    ただし、ファンシーポインタへの
    道のりは厳しい…

    View Slide

  91. 多相アロケータ
    アロケータおさらい
    91
    ファンシーポインタの要件
    ① NullablePointer 要件を満たすこと
    ② コンストラクタ、比較演算子、コピー、ムーブ、スワップの処理は例
    外を投げないこと
    ③ ランダムアクセスイテレータの要件を満たすこと
    ④ 連続イテレータの要件を満たすこと
    ①、➂、④は更にそれぞれ厳しい要件がある。
    標準にはこの要件を満たすファンシーポインタは存在しない。
    (shared_ptr とか unique_ptr は要件を満たさない)

    View Slide

  92. 多相アロケータ
    アロケータおさらい
    92
    ファンシーポインタっていつ使うの?

    View Slide

  93. 多相アロケータ
    アロケータおさらい
    93
    ファンシーポインタっていつ使うの?

    共有メモリとかのように、特殊なメモリを
    使用する時に使いたくなるらしい…

    View Slide

  94. 多相アロケータ
    アロケータおさらい
    94
    共有メモリだとプロセス毎にアドレスが異なる可能性があるので、
    普通のポインタ使うとデータが壊れる。
    そこで、ポインタオブジェクト自身のアドレスとポインタの指す対象オブ
    ジェクトのアドレスの差分を保持するオフセットポインタが有効。
    オフセットポインタ by Thomas Köppe
    また、光成さん(@herumi)もおっしゃっていたように、
    アドレス範囲を制限してすることによってポインタサイズを
    小さくするようなファンシーポインタも考えられる。
    しかし、std::list などは通常番兵ノードを置くが、番兵ノードはアロケータ
    でアロケートせずにオブジェクト本体内に配置することが多い。
    アロケータでアロケートしていないオブジェクトへのポインタをこの手の
    ポインタで指すと、問題が発生する。
    D0773R1 Towards meaningful fancy pointers
    勉強会での
    話を基に追加

    View Slide

  95. 多相アロケータ
    アロケータおさらい
    95
    なお、単にポインタをクラス内にラップしただけのクラスで試
    したところ、Clang(libc++) も GCC(libstdc++) も少なくとも以
    下のような問題で正しくファンシーポインタをサポートしきれ
    ていないようでした。
    Clang:ファンシーポインタのデフォルト構築がヌルポインタ
    になることに依存している個所がある(そのような要件は無
    い)
    GCC:本物のポインタを引数にするコンストラクタに依存して
    いる個所がある(そのような要件は無い)
    勉強会での
    話を基に追加

    View Slide

  96. 多相アロケータ
    アロケータおさらい
    96
    多相アロケータでは、
    ファンシーポインタのサポートは
    していない。(後述)

    View Slide

  97. 多相アロケータ
    97
    詳細

    View Slide

  98. 多相アロケータ
    詳細
    98
    std::pmr::memory_resource
    実際のメモリ割当・解放処理
    を実装する、全てのクラスの
    抽象基底クラス

    View Slide

  99. 多相アロケータ
    詳細
    99
    class memory_resource {
    public:
    virtual ~memory_resource();
    // 公開インタフェース(いわゆるNVI)、polymorphic_allocator で使われる。
    void* allocate(size_t bytes, size_t alignment = alignof(max_align_t));
    void deallocate(void* p, size_t bytes, size_t alignment = alignof(max_align_t));
    bool is_equal(const memory_resource& other) const noexcept;
    private:
    // 仮想メンバ関数(ここでは純粋仮想)、派生クラスで実装する。
    virtual void* do_allocate(size_t bytes, size_t alignment) = 0;
    virtual void do_deallocate(void* p, size_t bytes, size_t alignment) = 0;
    virtual bool do_is_equal(const memory_resource& other) const noexcept = 0;
    };

    View Slide

  100. 多相アロケータ
    詳細
    100
    void* allocate(size_t bytes, size_t alignment = alignof(max_align_t));
    単に do_allocate を呼ぶだけ。
    NVIだからね…
    virtual void* do_allocate(size_t bytes, size_t alignment) = 0;
    サイズ bytes、アラインメント alignment の
    メモリを割り当てる。
    各具象派生クラスで実装する。

    View Slide

  101. 多相アロケータ
    詳細
    101
    void deallocate(void* p, size_t bytes, size_t alignment = alignof(max_align_t));
    単に do_deallocate を呼ぶだけ。
    NVIだ(ry
    virtual void do_deallocate(void* p, size_t bytes, size_t alignment) = 0;
    do_allocate で割り当てられたメモリ p を解放する。
    サイズ bytes、アラインメント alignment は
    割り当て時の値(正しく使われれば)。
    各具象派生クラスで実装する。

    View Slide

  102. 多相アロケータ
    詳細
    102
    bool is_equal(const memory_resource& other) const noexcept;
    単に do_is_equal を呼ぶだけ。
    N(ry
    virtual bool do_is_equal(const memory_resource& other) const noexcept = 0;
    *this で割り当てられたメモリを other で解放したり、
    other で割り当てられたメモリを *this で解放したり
    できる場合のみ true を返す。
    各具象派生クラスで実装する。
    ふつうは this == &other で良いはず。

    View Slide

  103. 多相アロケータ
    詳細
    103
    ポインタが void* であることに注意!
    (アロケータはメンバ型 pointer)

    ファンシーポインタのサポートは
    諦めた、とのこと…※
    クラス継承構造だからね、仕方ないね…
    (派生クラスでメンバ関数の戻り値型変更は無理)
    ※ CppCon 2017: Alisdair Meredith “An allocator model for std2” 32:37

    View Slide

  104. 多相アロケータ
    詳細
    104
    polymorphic_allocator
    メンバ関数

    View Slide

  105. 多相アロケータ
    詳細
    105
    コンストラクタ
    // デフォルトコンストラクタ。memory_resource はシステムデフォルトを使用(後述)
    polymorphic_allocator() noexcept;
    // memory_resource へのポインタからの暗黙変換コンストラクタ。
    // アロケータが必要な個所で memory_resource が直接使えるようになるので便利。
    polymorphic_allocator(memory_resource*);
    // コピーコンストラクタ。other の memory_resource をコピーして使用。
    polymorphic_allocator(const polymorphic_allocator& other) = default;
    template
    polymorphic_allocator(const polymorphic_allocator& other) noexcept;
    見ての通り、ムーブコンストラクタは無いよ!
    (memory_resource のハンドラだからムーブされると困る…)

    View Slide

  106. 多相アロケータ
    詳細
    106
    代入演算子など
    // 代入演算子は無い!(コピー代入もムーブ代入も出来ない)
    polymorphic_allocator& operator=(const polymorphic_allocator& rhs) = delete;
    // コンテナをコピーする際のアロケータ取得時に呼ばれるメンバ関数。
    // デフォルト構築された新たな polymorphic_allocator オブジェクトを返す。
    // ちなみに、自分自身を返すのが、アロケータのデフォルトの挙動。
    polymorphic_allocator select_on_container_copy_construction() const;

    View Slide

  107. 多相アロケータ
    詳細
    107
    メモリ割当・解放など
    // メモリ割当。 memory_resource の allocate 呼び出してキャストして返すだけ。
    // サイズは n * sizeof(Tp)、アラインメントは alignof(Tp)。
    Tp* allocate(size_t n);
    // メモリ解放。 memory_resource の deallocate 呼び出すだけ。
    // サイズは n * sizeof(Tp)、アラインメントは alignof(Tp)。
    void deallocate(Tp* p, size_t n);
    // memory_resource の取得。
    // 単にコンストラクタで設定した memory_resource を返すだけ。
    memory_resource* resource() const;

    View Slide

  108. 多相アロケータ
    詳細
    108
    オブジェクト構築
    // コンテナ内でのオブジェクト構築。(emplace 的なヤツ)
    // 引数 p で指定された場所に、T 型のオブジェクトを、
    // *this と std::forward(args)… で uses-allocator 構築する。
    // T が std::pair の特殊化の場合は使用されない
    template
    void construct(T* p, Args&&... args);

    View Slide

  109. 多相アロケータ
    詳細
    109
    オブジェクト構築(続き)
    // 要素型が pair の場合の特別なオブジェクト構築
    // pair の first と second をそれぞれうまい具合に uses-allocator 構築する
    template
    void construct(pair* p, piecewise_construct_t,
    tuple x, tuple y);
    template
    void construct(pair* p);
    template
    void construct(pair* p, U&& x, V&& y);
    template
    void construct(pair* p, const pair& pr);
    template
    void construct(pair* p, pair&& pr);

    View Slide

  110. 多相アロケータ
    詳細
    110
    オブジェクト破棄
    // オブジェクト破棄。単にデストラクタ呼ぶだけ。
    template
    void destroy(T* p);

    View Slide

  111. 多相アロケータ
    詳細
    111
    その他
    // 使用しているメモリリソースの取得。
    memory_resource* resource() const;

    View Slide

  112. 多相アロケータ
    詳細
    112
    その他(非メンバ関数)
    // アロケータの比較。*a.resource() == *b.resource() の結果を返すだけ。
    template
    bool operator==(const polymorphic_allocator& a,
    const polymorphic_allocator& b) noexcept;
    // アロケータの比較。*a.resource() != *b.resource() の結果を返すだけ。
    template
    bool operator!=(const polymorphic_allocator& a,
    const polymorphic_allocator& b) noexcept;

    View Slide

  113. 多相アロケータ
    113
    標準ライブラリが提供している
    memory_resource の具象クラス

    View Slide

  114. 多相アロケータ
    memory_resource の具象クラス
    114
    // グローバルオブジェクト(クラス名は未規定)
    memory_resource* new_delete_resource() noexcept;
    memory_resource* null_memory_resource() noexcept;
    // 特殊用途用クラス
    class synchronized_pool_resource;
    class unsynchronized_pool_resource;
    class monotonic_buffer_resource;

    View Slide

  115. 多相アロケータ
    memory_resource の具象クラス
    115
    標準が提供している
    memory_resource の具象クラス①
    memory_resource* new_delete_resource() noexcept;
    普通に ::operator new、::operator delete でメモリ割
    当、解放するヤツ。つまり極めて普通。
    この関数を呼ぶと、常に同じオブジェクトへのポイ
    ンタが返る。(自分でインスタンス生成とかしない)

    View Slide

  116. 多相アロケータ
    memory_resource の具象クラス
    116
    標準が提供している
    memory_resource の具象クラス②
    memory_resource* null_resource() noexcept;
    メモリ割当しようとすると常に bad_alloc 投げるヤツ。
    つまり極めて変。他のと組み合わせて使うと有用。
    この関数を呼ぶと、常に同じオブジェクトへのポイ
    ンタが返る。(自分でインスタ(ry

    View Slide

  117. 多相アロケータ
    memory_resource の具象クラス
    117
    標準が提供している
    memory_resource の具象クラス➂
    class synchronized_pool_resource;
    メモリプールを使用するヤツ。実際に使用するメモ
    リは上位メモリリソースから割り当てられる。
    外部同期無しで複数スレッドから同時使用可能。

    View Slide

  118. 多相アロケータ
    memory_resource の具象クラス
    118
    synchronized_pool_resource イメージ
    サイズ
    ~16
    17~32
    33~64
    65~128
    プールはブロックサイ
    ズ毎に分かれている。
    (ただし、上限あり)
    メモリ割当は指定さ
    れたサイズ以上で最
    小のブロックサイズ
    のプールから。
    各プールのメモリは、同一サイズのブロックを複数まとめたチャ
    ンクに分割されている。プール内のメモリを使い果たすと、新た
    なチャンクを上位メモリリソースから確保する。その際、チャンク
    のサイズは等比級数的に増大していく。(ただし、上限あり)
    割当済 未割当 次に上位から確保

    View Slide

  119. 多相アロケータ
    memory_resource の具象クラス
    119
    なお、synchronized_pool_resource
    では、スレッド間の同期コストを削
    減するために、スレッド毎にメモリ
    プールがあるかもしれない…
    無いかもしれない…
    でもスレッド毎にプール持ったら、違うスレッドから解放された場合どうするんだろう…

    View Slide

  120. 多相アロケータ
    memory_resource の具象クラス
    120
    synchronized_pool_resource コンストラクタ①
    synchronized_pool_resource(
    const pool_options& opts,
    memory_resource* upstream);
    引数のいずれか、あるいは両方を省いたバージョンもある。(引数1つの
    バージョンは explicit コンストラクタ)
    opts:挙動調整用のオプション。後述。
    upstream:上位メモリリソースへのポインタ。このメモリリソースから割り
    当てられるメモリは全て upstream から取得される。指定しないとデフォ
    ルトメモリリソース(後述)を指定したことになる。

    View Slide

  121. 多相アロケータ
    memory_resource の具象クラス
    121
    struct pool_options {
    size_t max_blocks_per_chunk = 0;
    size_t largest_required_pool_block = 0;
    };
    max_blocks_per_chunk:チャンクの最大ブロック数。チャンクサイズは、各プール
    のメモリが枯渇して上位メモリリソースに確保に行くたびに等比級数的に増加し
    ていくが、増加はここで指定したブロック数を上限とする。
    largest_required_pool_block:プールするメモリサイズの上限。この値を超えるメ
    モリ割り当て要求は上位メモリリソースから直接割当し、プールでの管理は行わ
    ない。
    いずれも、0(デフォルト値)を指定すると実装依存の上限値となる。また、指定
    値がデカすぎる場合も適当に調整される。

    View Slide

  122. 多相アロケータ
    memory_resource の具象クラス
    122
    synchronized_pool_resource コンストラクタ②
    コピーコンストラクタ、ムーブコンストラクタは無い。
    ちなみに、コピー代入演算子、ムーブ代入演算子も
    無い。
    つまり、コピー、ムーブは一切できない。

    View Slide

  123. 多相アロケータ
    memory_resource の具象クラス
    123
    synchronized_pool_resource メンバ関数①
    void release();
    管理している全てのメモリを、たとえ deallocate が
    呼び出されていなくても、上位メモリリソースに返す。

    View Slide

  124. 多相アロケータ
    memory_resource の具象クラス
    124
    synchronized_pool_resource メンバ関数②
    pool_options options() const;
    プールのオプションを返す。ただし、デフォルト値 0
    が実際の実装依存の値になったり、指定された値
    が実装によって調整されたりする可能性があるの
    で、コンストラクタで指定された値とは違うかもしれ
    ない。

    View Slide

  125. 多相アロケータ
    memory_resource の具象クラス
    125
    synchronized_pool_resource メンバ関数➂
    memory_resource* upstream_resource() const;
    上位メモリリソースへのポインタを返す。極めて普
    通。

    View Slide

  126. 多相アロケータ
    memory_resource の具象クラス
    126
    synchronized_pool_resource 仮想関数①
    void* do_allocate(
    size_t bytes, size_t alignment) override;
    指定されたサイズ、指定されたアラインメントのメモ
    リをプールから、あるいは上位メモリリソースから直
    接割り当てる。極めて普通。

    View Slide

  127. 多相アロケータ
    memory_resource の具象クラス
    127
    synchronized_pool_resource 仮想関数②
    void do_deallocate(void* p,
    size_t bytes, size_t alignment) override;
    割り当てられたメモリをプールに返す。
    なお、上位メモリリソースの deallocate が呼び出さ
    れるか否かは未規定。

    View Slide

  128. 多相アロケータ
    memory_resource の具象クラス
    128
    synchronized_pool_resource 仮想関数➂
    bool do_is_equal(const memory_resource& other)
    const noexcept override;
    指定されたメモリリソースが自分と互換性のあるメ
    モリリソースかどうかを返す。
    ゆうても、単に this == &other の結果を返すだけ。

    View Slide

  129. 多相アロケータ
    memory_resource の具象クラス
    129
    synchronized_pool_resource デストラクタ
    ~synchromized_pool_resource();
    release() メンバ関数を呼ぶ。つまり、全メモリが解
    放される。まぁ当たり前っちゃあ当たり前。
    このメモリリソースから割り当てられたメモリを使用
    しているコンテナ等よりも、このメモリリソースの生
    存期間が短くならないように注意が必要。

    View Slide

  130. 多相アロケータ
    memory_resource の具象クラス
    130
    標準が提供している
    memory_resource の具象クラス④
    class unsynchronized_pool_resource;
    synchronized_pool_resource のシングルスレッド版。
    外部同期無しで複数スレッドから同時使用できない
    こと以外は synchronized_pool_resource と一緒。
    なので説明(ry

    View Slide

  131. 多相アロケータ
    memory_resource の具象クラス
    131
    標準が提供している
    memory_resource の具象クラス➄
    class monotonic_buffer_resource;
    メモリ割当するだけして、絶対解放しないヤツ。
    つまりメモリ使用量が単調(monotonic)増加。
    同期とかガチ無視なので、まぁ単一スレッド用。
    使用用途が限られるがちょっぱや。

    View Slide

  132. 多相アロケータ
    memory_resource の具象クラス
    132
    monotonic_buffer_resource イメージ
    メモリは端から順に割り当て
    ていくだけ。解放は無視。
    足りなくなったら上位メモリリ
    ソースから新たなメモリを確
    保するが、その際のサイズ
    は等比級数的に増加する。
    割当済
    未割当
    次に上位から確保
    未割当ポインタ

    View Slide

  133. 多相アロケータ
    memory_resource の具象クラス
    133
    monotonic_buffer_resource コンストラクタ①
    monotonic_buffer_resource(
    size_t initial_size, memory_resource* upstream);
    引数のいずれか、あるいは両方を省いたバージョンもある。(引数1つの
    バージョンは explicit コンストラクタ)
    initial_size:最初に上位メモリリソースから確保するメモリのサイズ。実
    装によって調整が入るかもしれない。指定が無い場合は実装依存。
    upstream:上位メモリリソースへのポインタ。このメモリリソースから割り
    当てられるメモリは全て upstream から取得される。指定しないとデフォ
    ルトメモリリソース(後述)を指定したことになる。

    View Slide

  134. 多相アロケータ
    memory_resource の具象クラス
    134
    monotonic_buffer_resource コンストラクタ②
    monotonic_buffer_resource(void* buffer, size_t buffer_size,
    memory_resource* upstream);
    upstream を省いたバージョンもある。
    buffer:初期の割当用メモリ領域。
    buffer_size:buffer の容量。また、次に上位メモリリソースから確保するメ
    モリのサイズは、この値を等比級数的に増加させた値になる。
    upstream:上位メモリリソースへのポインタ。buffer を使い果たしたら、
    upstream から取得される。指定しないとデフォルトメモリリソース(後述)
    を指定したことになる。

    View Slide

  135. 多相アロケータ
    memory_resource の具象クラス
    135
    monotonic_buffer_resource コンストラクタ➂
    コピーコンストラクタ、ムーブコンストラクタは無い。
    ちなみに、コピー代入演算子、ムーブ代入演算子も
    無い。
    つまり、コピー、ムーブは一切できない。

    View Slide

  136. 多相アロケータ
    memory_resource の具象クラス
    136
    monotonic_buffer_resource メンバ関数①
    void release();
    管理している全てのメモリを、たとえ deallocate が
    呼び出されていなくても、上位メモリリソースに返す。

    View Slide

  137. 多相アロケータ
    memory_resource の具象クラス
    137
    monotonic_buffer_resource メンバ関数②
    memory_resource* upstream_resource() const;
    上位メモリリソースへのポインタを返す。極めて普
    通。

    View Slide

  138. 多相アロケータ
    memory_resource の具象クラス
    138
    monotonic_buffer_resource 仮想関数①
    void* do_allocate(
    size_t bytes, size_t alignment) override;
    指定されたサイズ、指定されたアラインメントのメモ
    リを割り当てる。極めて普通。

    View Slide

  139. 多相アロケータ
    memory_resource の具象クラス
    139
    monotonic_buffer_resource 仮想関数②
    void do_deallocate(void* p,
    size_t bytes, size_t alignment) override;
    メモリを解放?いや、そんなことはしない。
    とにかく何もしない。

    View Slide

  140. 多相アロケータ
    memory_resource の具象クラス
    140
    monotonic_buffer_resource 仮想関数➂
    bool do_is_equal(const memory_resource& other)
    const noexcept override;
    指定されたメモリリソースが自分と互換性のあるメ
    モリリソースかどうかを返す。
    ゆうても、単に this == &other の(ry

    View Slide

  141. 多相アロケータ
    memory_resource の具象クラス
    141
    monotonic_buffer_resource デストラクタ
    ~monotonic_buffer_resource();
    release() メンバ関数を呼ぶ。つまり、全メモリが解
    放される。まぁ当たり前っちゃあ当たり前。
    このメモリリソースから割り当て(ry

    View Slide

  142. 多相アロケータ
    142
    その他のユーティリティ

    View Slide

  143. 多相アロケータ
    その他
    143
    グローバルなユーティリティ①
    memory_resource* set_default_resource(memory_resource* r) noexcept;
    デフォルトのメモリリソースを引数 r に設定して、今
    まで設定されてたメモリリソースを返す。
    r が nullptr なら new_delete_resource を設定する。
    ちなみに、システムの初期状態は
    new_delete_resource になっている。
    普通っぽい…

    View Slide

  144. 多相アロケータ
    その他
    144
    グローバルなユーティリティ②
    memory_resource* get_default_resource() noexcept;
    現在のデフォルトのメモリリソースを取得する。

    View Slide

  145. 多相アロケータ
    その他
    145
    グローバルなユーティリティ➂
    bool operator==(const memory_resource& a,
    const memory_resource& b) noexcept;
    メモリリソースを比較する。
    単に &a == &b || a.is_equal(b) を返すだけ。
    こいつの引数はポインタじゃないので注意。

    View Slide

  146. 多相アロケータ
    その他
    146
    グローバルなユーティリティ④
    bool operator!=(const memory_resource& a,
    const memory_resource& b) noexcept;
    メモリリソースを比較する。
    みんなの予想通りの挙動。(たぶん)
    こいつの引数もポインタじゃない。

    View Slide

  147. 多相アロケータ
    147
    デメリット

    View Slide

  148. 多相アロケータ
    デメリット
    148
    • アロケータオブジェクト毎に
    memory_resource へのポインタを持ってい
    るため、標準アロケータを使った時より各
    オブジェクトがポインタ 1 つ分大きくなる。
    • メモリ割当、解放が仮想関数になっている
    ので、標準アロケータを使った時より関数
    呼び出しオーバーヘッドが大きくなる。

    View Slide

  149. 多相アロケータ
    149
    悲報

    View Slide

  150. 多相アロケータ
    悲報
    150
    GCC も Clang もまだ多相アロ
    ケータの実装が終わってない…
    MSVC2017 はぱっと見ありそうでした…
    (ただし、どれもちゃんと調査できてない…)
    勉強会時コメント by いなむ先生(@いなむのみたま)
    MSVC2017はちゃんと動くよ!

    View Slide

  151. 多相アロケータ
    悲報
    151
    GCC HEAD
    synchronized_pool_resource と
    unsynchronized_pool_resource が無い。
    その他はありそう…
    Clang HEAD
    メモリリソースはグローバルなヤツしかないし、
    そもそもまだ experimental。
    自分でメモリリソース作るなら一応どっちも行けそう…

    View Slide

  152. 多相アロケータ
    悲報
    152
    しばらくは Boost.Container 使う
    しかないね…
    でも Boost.Container も pool_options がちょっと変だった…
    なぜかデフォルトコンストラクタだけがあるから、
    synchronized_pool_resource のコンストラクタに初期化リストが直接書けない…

    View Slide

  153. 多相アロケータ
    参考資料
    153
    [email protected]++11 by Cryolite 先生
    C++11 でのアロケータ機能拡充に関する神スライド
    https://www.slideshare.net/Cryolite/allocator11final
    A visitor’s guide to C++ allocators by Thomas Köppe
    アロケータの詳細がまとめられた非常に良い記事
    カスタムアロケータやオフセットポインターのサンプル実装へのリンクもある
    https://rawgit.com/google/cxx-std-draft/allocator-paper/allocator_user_guide.html
    D0773R1 Towards meaningful fancy pointers
    ファンシーポインタとその適用限界について考察した非常に良い記事
    https://quuxplusone.github.io/draft/fancy-pointers.html
    勉強会後に
    追記

    View Slide

  154. 多相アロケータ
    参考資料
    154
    CppCon 2017 でのアロケータに関する講演
    Pablo Halpern “Allocators: The Good Parts”
    https://www.youtube.com/watch?v=v3dz-AKOVL8
    Alisdair Meredith “An allocator model for std2”
    https://www.youtube.com/watch?v=oCi_QZ6K_qk
    John Lakos “Local ('Arena') Memory Allocators (part 1 of 2)”
    https://www.youtube.com/watch?v=nZNd5FjSquk
    John Lakos “Local ('Arena') Memory Allocators (part 2 of 2)”
    https://www.youtube.com/watch?v=CFzuFNSpycI
    Bob Steagall “How to Write a Custom Allocator”
    https://www.youtube.com/watch?v=kSWfushlvB8
    勉強会後に
    追記

    View Slide

  155. 多相アロケータ
    155

    View Slide