最終更新日時: 2026年04月09日 08時58分08秒 (JST)
Akira Takahashi が更新

履歴 編集

function
<memory_resource>

# std::pmr::polymorphic_allocator::construct(C++17)

    template <class T, class... Args>
    void construct(T* p, Args&&... args);                   //(1)

    template <class T1, class T2, class... Args1, class... Args2>
    void construct(pair<T1, T2>* p, piecewise_construct_t,
                   tuple<Args1...> x, tuple<Args2...> y);   //(2) C++17 まで

    template <class T1, class T2>
    void construct(pair<T1, T2>* p);                        //(3) C++17 まで

    template <class T1, class T2, class U, class V>
    void construct(pair<T1, T2>* p, U&& x, V&& y);          //(4) C++17 まで

    template <class T1, class T2, class U, class V>
    void construct(pair<T1, T2>* p, const pair<U, V>& pr);  //(5) C++17 まで

    template <class T1, class T2, class U, class V>
    void construct(pair<T1, T2>* p, pair<U, V>&& pr);       //(6) C++17 まで

## 概要

`p` で指定された領域に、`*this` と指定された引数で uses-allocator 構築を行う。
また、`*p` が `pair` だった場合は、それぞれの要素に対して `*this` と指定された引数で uses-allocator 構築を行う。

## 適格要件

- (1) : `*this` と `args...` をコンストラクタ引数とした uses-allocator 構築が可能であること。
  アロケータを受け取るコンストラクタを持たない型については、（`args..` が適切ならば）この要件を常に満たしている。
  C++17までは、この関数は `T` が `pair` の特殊化でない場合に限りオーバーロード解決に参加する。

## 引数

- 全て : `p` -- `T` もしくは `pair<T1, T2>` のオブジェクトを構築するメモリ領域へのポインタ

- (1) : `args` -- `T` のコンストラクタに渡す引数列

- (2) :

  - `x` -- `T1` のコンストラクタに渡す引数を持つ `tuple` オブジェクト
  - `y` -- `T2` のコンストラクタに渡す引数を持つ `tuple` オブジェクト

- (4) :

  - `x` -- `T1` のコンストラクタに渡す引数
  - `y` -- `T2` のコンストラクタに渡す引数

- (5) : `pr` -- `T1, T2` のコンストラクタにそれぞれコピーして渡す引数 `U, V` のオブジェクトを持つ `pair<U, V>`オブジェクト

- (6) : `pr` -- `T1, T2` のコンストラクタにそれぞれ `forward` して渡す引数 `U, V` のオブジェクトを持つ `pair<U, V>` オブジェクト

## 効果

- (1) :

  - C++17 まで : 以下のいずれかの動作を行う。
    - `uses_allocator_v<T, polymorphic_allocator> == false` かつ `is_constructible_v<T, Args...> == true` の場合
      `::new(static_cast<void*>(p)) T(forward<Args>(args)...)` を呼び出す。
    - `uses_allocator_v<T, polymorphic_allocator> == true` かつ `is_constructible_v<T, allocator_arg_t, polymorphic_allocator, Args...> == true` の場合
      `::new(static_cast<void*>(p)) T(allocator_arg, *this, forward<Args>(args)...)` を呼び出す。
    - `uses_allocator_v<T, polymorphic_allocator> == true` かつ `is_constructible_v<T, Args..., polymorphic_allocator> == true` の場合
      `::new(static_cast<void*>(p)) T(forward<Args>(args)..., *this)`
    - それ以外の場合、プログラムは不適格となる。

  - C++20 から : 以下と等価の動作を行う。

        uninitialized_construct_using_allocator(p, *this, forward<Args>(args)...)

- (2) : 以下と等価

      ::new(static_cast<void*>(p)) pair<T1, T2>(piecewise_construct, xprime, yprime)

  ここで、`xprime` は以下のルールに従って `x` から構築される `tuple` とする。（`yprime` も `y` から同様に構築されるものとする）

  - `uses_allocator_v<T1, polymorphic_allocator> == false` かつ `is_constructible_v<T1, Args1...> == true` の場合
    `x` を `xprime` とする。
  - `uses_allocator_v<T1, polymorphic_allocator> == true` かつ `is_constructible_v<T1, allocator_arg_t, polymorphic_allocator, Args1...> == true` の場合
    `tuple_cat(make_tuple(allocator_arg, *this), move(x))` を `xprime` とする。
  - `uses_allocator_v<T1, polymorphic_allocator> == true` かつ `is_constructible_v<T1, Args1..., polymorphic_allocator> == true` の場合
    `tuple_cat(move(x), make_tuple(*this))` を `xprime` とする。
  - それ以外の場合、プログラムは不適格となる。

- (3) : 以下と等価、すなわち(2)に委譲

      construct(p, piecewise_construct, tuple<>(), tuple<>());

- (4) : 以下と等価、すなわち(2)に委譲

      construct(p, piecewise_construct,
                forward_as_tuple(forward<U>(x)),
                forward_as_tuple(forward<V>(y)));

- (5) : 以下と等価、すなわち(2)に委譲

      construct(p, piecewise_construct,
                forward_as_tuple(pr.first),
                forward_as_tuple(pr.second));

- (6) : 以下と等価、すなわち(2)に委譲

      construct(p, piecewise_construct,
                forward_as_tuple(forward<U>(pr.first)),
                forward_as_tuple(forward<V>(pr.second)));

## 例外

- (1) : `T`のコンストラクタが例外を投げないならば、この関数も例外を投げない。

## 備考

- C++20 における変更は、一見新規導入されたユーティリティ関数（`uninitialized_construct_using_allocator`）を使用して定義を簡略化しただけのように思えるが、実はこの変更によりネストした `pair` に対しても uses-allocator 構築がサポートされるように改善されている。

## (1)の例

    #include <iostream>
    #include <memory_resource>

    int main()
    {
      std::pmr::polymorphic_allocator<int> alloc{};

      //メモリの確保
      int* array = alloc.allocate(4);

      //要素を構築
      for (int i = 0; i < 4; ++i) {
        alloc.construct(array + i, i);
      }

      for (int i = 0; i < 4; ++i) {
        std::cout << array[i] << std::endl;
      }

      //要素を破棄
      for (int i = 0; i < 4; ++i) {
        alloc.destroy(array + i);
      }

      //メモリの解放
      alloc.deallocate(array, 4);
    }

### 出力

    0
    1
    2
    3

## `pair`関連のオーバーロードの例

    #include <iostream>
    #include <memory_resource>
    #include <string>
    #include <tuple>
    #include <utility>

    int main()
    {
      //intとpolymorphic_allocatorを用いるstringのpair
      using pair = std::pair<int, std::pmr::string>;

      //memory_resourceとしてmonotonic_buffer_resourceを使用
      auto mr = std::pmr::monotonic_buffer_resource{};
      std::pmr::polymorphic_allocator<pair> alloc{&mr};

      std::cout << std::boolalpha;
      std::cout << "(2)" << std::endl;

      //(2)
      {
        pair* p = alloc.allocate(1);

        alloc.construct(p, std::piecewise_construct
            , std::make_tuple(128)         //intを128で初期化
            , std::make_tuple("string", 3) //string("string", 3)で初期化（最初の3文字を保持する）
        );

        std::cout << p->first << std::endl;
        std::cout << p->second << std::endl;
        //アロケータが伝播している
        std::cout << (p->second.get_allocator() == alloc) << std::endl;
      }

      std::cout << "\n(3)" << std::endl;

      //(3)
      {
        pair* p = alloc.allocate(1);

        alloc.construct(p);  //両要素をデフォルト構築

        std::cout << p->first << std::endl;
        std::cout << p->second << std::endl;
        //アロケータが伝播している
        std::cout << (p->second.get_allocator() == alloc) << std::endl;
      }

      std::cout << "\n(4)" << std::endl;

      //(4)
      {
        pair* p = alloc.allocate(1);

        alloc.construct(p, 128, "string");  //両要素をこれらの値からムーブ構築

        std::cout << p->first << std::endl;
        std::cout << p->second << std::endl;
        //アロケータが伝播している
        std::cout << (p->second.get_allocator() == alloc) << std::endl;
      }

      std::cout << "\n(5)" << std::endl;

      //(5)
      {
        pair* p = alloc.allocate(1);

        const auto v = std::make_pair(128, "copy");
        alloc.construct(p, v);  //両要素を変換可能なpairからコピー構築

        std::cout << p->first << std::endl;
        std::cout << p->second << std::endl;
        //アロケータが伝播している
        std::cout << (p->second.get_allocator() == alloc) << std::endl;
      }

      std::cout << "\n(6)" << std::endl;

      //(6)
      {
        pair* p = alloc.allocate(1);

        alloc.construct(p, std::make_pair(128, "move"));  //両要素を変換可能なpairからムーブ構築

        std::cout << p->first << std::endl;
        std::cout << p->second << std::endl;
        //アロケータが伝播している
        std::cout << (p->second.get_allocator() == alloc) << std::endl;
      }
    }

### 出力

    (2)
    128
    str
    true

    (3)
    0

    true

    (4)
    128
    string
    true

    (5)
    128
    copy
    true

    (6)
    128
    move
    true

## ネストした `pair` の例

    #include <iostream>
    #include <string>
    #include <utility>
    #include <memory_resource>

    int main()
    {
      using pair = std::pair<std::pair<int, std::pmr::string>, std::pmr::string>;

      auto mr = std::pmr::monotonic_buffer_resource{};
      std::pmr::polymorphic_allocator<pair> alloc{&mr};

      std::cout << std::boolalpha;

      pair* p = alloc.allocate(1);

      alloc.construct(p);

      std::cout << (p->first.second.get_allocator() == alloc) << '\n';
      std::cout << (p->second.get_allocator() == alloc) << '\n';

      alloc.destroy(p);

      alloc.deallocate(p, 1);
    }

### C++17 における出力

    false
    true

### C++20 における出力

    true
    true

## バージョン

### 言語

- C++17

### 処理系

- Clang: ??
- GCC: 9.1 ✅
- Visual C++: 2017 update 6 ✅
  - 2017, 2019共に(1)以外のオーバーロードを提供していないが、`pair` の各要素に対する uses-allocator 構築をサポートしている。（ただし、C++20 モードでもネストした `pair` はサポートされていない）

## 関連項目

- `construct`
- `uses_allocator`

## 参照

- P0220R1 Adopt Library Fundamentals V1 TS Components for C++17 (R1)
- P0337r0 | Delete operator= for polymorphic_allocator
- Working Draft, C++ Extensions for Library Fundamentals, Version 2
- LWG Issue 2969. `polymorphic_allocator::construct()` shouldn't pass resource()
- LWG Issue 2975. Missing case for pair construction in scoped and polymorphic allocators