最終更新日時: 2025年07月09日 00時33分39秒 (JST)
Akira Takahashi が更新

履歴 編集

function template
<scoped_allocator>

# std::scoped_allocator_adaptor::construct(C++11)

    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>& x);         // (5) C++17 まで

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

## 概要

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

## 効果

説明を簡略化するために、以下の説明用関数を使用する。

- `OUTERMOST(x)` は、`x` が `outer_allocator()` メンバ関数を持っている場合は `OUTERMOST(x.outer_allocator())` を、持っていない場合は `x` を返す。

- `CONSTRUCT(...)` は、`allocator_traits<decltype(OUTERMOST(*this))>::construct(OUTERMOST(*this), p, ...)` とする。

- (1) :

  - C++17 まで : 以下のいずれかの動作を行う。
    - `uses_allocator<T, inner_allocator_type>::value == false` かつ `is_constructible<T, Args...>::value == true` の場合
      `CONSTRUCT(forward<Args>(args)...)` を呼び出す。
    - `uses_allocator<T, inner_allocator_type>::value == true` かつ `is_constructible<T, allocator_arg_t, inner_allocator_type, Args...>::value == true` の場合
      `CONSTRUCT(allocator_arg, inner_allocator(), forward<Args>(args)...)` を呼び出す。
    - `uses_allocator<T, inner_allocator_type>::value == true` かつ `is_constructible<T, Args..., inner_allocator_type>::value == true` の場合
      `CONSTRUCT(forward<Args>(args)..., inner_allocator())`を呼び出す。
    - それ以外の場合、プログラムは不適格となる。
    - この関数は `T` が `pair` の特殊化でない場合に限りオーバーロード解決に参加する。

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

        apply(
          [p, this](auto&&... newargs) {
            CONSTRUCT(forward<decltype(newargs)>(newargs)...);
          },
          uses_allocator_construction_args<T>(inner_allocator(), forward<Args>(args)...)
        );

- (2) : 以下と等価の動作を行う。
  `CONSTRUCT(piecewise_construct, move(xprime), move(yprime))`
  ここで、`xprime` は以下のルールに従って `x` から構築された `tuple` とする。（`yprime` も `y` から同様に構築されるものとする）

  - `uses_allocator<T1, inner_allocator_type>::value == false` かつ `is_constructible<T1, Args1...>::value == true` の場合
    `x` を `xprime` とする。
  - `uses_allocator<T1, inner_allocator_type>::value == true` かつ `is_constructible<T1, allocator_arg_t, inner_allocator_type, Args1...>::value == true` の場合
    `tuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator_type()), x)` を `xprime` とする。
  - `uses_allocator<T1, inner_allocator_type>::value == true` かつ `is_constructible<T1, Args1..., inner_allocator_type>::value == true` の場合
    `tuple_cat(x, tuple<inner_allocator_type&>(inner_allocator_type()))` を `xprime` とする。
  - それ以外の場合、プログラムは不適格となる。

- (3) : 以下と等価の動作を行う。
  `construct(p, piecewise_construct, tuple<>(), tuple<>())`

- (4) : 以下と等価の動作を行う。
  `construct(p, piecewise_construct, forward_as_tuple(forward<U>(x)), forward_as_tuple(forward<V>(y)))`

- (5) : 以下と等価の動作を行う。
  `construct(p, piecewise_construct, forward_as_tuple(x.first), forward_as_tuple(x.second))`

- (6) : 以下と等価の動作を行う。
  `construct(p, piecewise_construct, forward_as_tuple(forward(x.first)), forward_as_tuple(forward(x.second)))`

## 備考

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

## 例

    #include <cassert>
    #include <vector>
    #include <string>
    #include <scoped_allocator>

    template <class T>
    using alloc_t = std::allocator<T>;

    // コンテナの要素(Inner)
    using string = std::basic_string<
      char,
      std::char_traits<char>,
      alloc_t<char>
    >;

    // コンテナ(Outer)
    template <class T>
    using vector = std::vector<
      T,
      std::scoped_allocator_adaptor<alloc_t<T>, alloc_t<typename T::value_type>>
    >;

    template <class T>
    using pair_of_vector = std::vector<
      T,
      std::scoped_allocator_adaptor<alloc_t<T>>
    >;

    // (1)
    void construct_propagate_alloc()
    {
      vector<string>::allocator_type alloc {
        alloc_t<string>(), // vector自体のアロケータオブジェクト
        alloc_t<char>()    // vectorの全ての要素に使用するアロケータオブジェクト
      };

      // 外側のアロケータを使用し、stringが1要素入るメモリを確保
      const std::size_t n = 1;
      string* p = alloc.allocate(n);

      // (1) 以下のコンストラクタを呼び出し、アロケータオブジェクトを伝播させる
      // basic_string(const char*, Allocator)
      alloc.construct(p, "hello");

      // オブジェクトを破棄
      alloc.destroy(p);

      // メモリを解放
      alloc.deallocate(p, n);
    }

    void construct_pair()
    {
      pair_of_vector<std::pair<string, string>>::allocator_type alloc;

      const std::size_t n = 5;
      std::pair<string, string>* p = alloc.allocate(n);

      // (2)
      // pairの各要素に対して以下のコンストラクタを呼び出し、
      // アロケータオブジェクトを伝播させる。
      // basic_string(const char*, Allocator)
      std::pair<string, string>* pair_p = p;
      alloc.construct(p, std::piecewise_construct,
                      std::forward_as_tuple("hello"),
                      std::forward_as_tuple("world"));
      assert(pair_p->first == "hello");
      assert(pair_p->second == "world");

      // (3)
      // pairの要素をデフォルト構築する。
      pair_p = std::next(pair_p);
      alloc.construct(pair_p);
      assert(pair_p->first == "");
      assert(pair_p->second == "");

      // (4)
      // pairの各要素のコンストラクタ引数をひとつずつ受け取って構築
      pair_p = std::next(pair_p);
      alloc.construct(pair_p, "hello", "world");
      assert(pair_p->first == "hello");
      assert(pair_p->second == "world");

      // (5)
      // pairの各要素のコンストラクタ引数をひとつずつ、
      // まとめてpairとして受け取り、それぞれをtupleに分解して構築
      pair_p = std::next(pair_p);
      std::pair<const char*, const char*> fifth_args("hello", "world");
      alloc.construct(pair_p, fifth_args);

      // (6)
      // pairの各要素のコンストラクタ引数をひとつずつ、
      // まとめてpairとして受け取り、それぞれをtupleに分解して転送して構築
      pair_p = std::next(pair_p);
      alloc.construct(pair_p, std::make_pair("hello", "world"));

      for (std::size_t i = 0; i < n; ++i) {
        alloc.destroy(p + i);
      }

      alloc.deallocate(p, n);
    }

    int main()
    {
      construct_propagate_alloc();
      construct_pair();
    }

### 出力

## バージョン

### 言語

- C++11

### 処理系

- Clang: 3.3 (`forward_as_tuple()`まで含めた完全な実装) ✅
- GCC: 4.7.3 ✅
- ICC: ??
- Visual C++: ??

## 関連項目

- `uses_allocator_construction_args`
- `make_obj_using_allocator`
- `uninitialized_construct_using_allocator`
- `uses_allocator`
- `polymorphic_allocator`
- `pair`

## 参照

- P0591R4 Utility functions to implement uses-allocator construction
- LWG Issue 2975. Missing case for pair construction in scoped and polymorphic allocators