- memory_resource[meta header]
- function[meta id-type]
- std::pmr[meta namespace]
- polymorphic_allocator[meta class]
- cpp17[meta cpp]
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)
template <class T1, class T2>
void construct(pair<T1,T2>* p); //(3)
template <class T1, class T2, class U, class V>
void construct(pair<T1,T2>* p, U&& x, V&& y); //(4)
template <class T1, class T2, class U, class V>
void construct(pair<T1,T2>* p, const pair<U, V>& pr); //(5)
template <class T1, class T2, class U, class V>
void construct(pair<T1,T2>* p, pair<U, V>&& pr); //(6)- pair[link /reference/utility/pair.md]
- tuple[link /reference/tuple/tuple.md]
- piecewise_construct_t[link /reference/utility/piecewise_construct_t.md]
指定されたpのメモリ領域にTのオブジェクトを構築する。
-
(1) : 「アロケータを使用する構築」により
pのメモリ領域にargs...をコンストラクタ引数として、Tのオブジェクトを構築する -
(2) :
pのメモリ領域にstd::pair<T1, T2>のオブジェクトを、x, y内の要素をT1, T2それぞれのコンストラクタ引数として、T1, T2それぞれが「アロケータを使用する構築」により構築されるように、直接構築(piecewise construct)する -
(3) :
pのメモリ領域にstd::pair<T1, T2>のオブジェクトを、T1, T2それぞれが引数無しの「アロケータを使用する構築」により構築されるように、直接構築(piecewise construct)する -
(4) :
pのメモリ領域にstd::pair<T1, T2>のオブジェクトを、T1, T2のコンストラクタ引数としてそれぞれx, yをforwardして「アロケータを使用する構築」により構築されるように、直接構築(piecewise construct)する -
(5) :
pのメモリ領域にstd::pair<T1, T2>のオブジェクトを、T1, T2のコンストラクタ引数としてそれぞれpr.first, pr.secondをコピーして「アロケータを使用する構築」により構築されるように、直接構築(piecewise construct)する -
(6) :
pのメモリ領域にstd::pair<T1, T2>のオブジェクトを、T1, T2のコンストラクタ引数としてそれぞれpr.first, pr.secondをforwardして「アロケータを使用する構築」により構築されるように、直接構築(piecewise construct)する
アロケータを使用する構築とは、型Tのオブジェクトobjを構築する際に引数とアロケータをTのコンストラクタに適切に引き渡すための仕組みである。
アロケータのオブジェクトをalloc、型をAllocとし、引数列をv1, v2, ..., vN、その型をV1, V2, ..., VNとすると
-
Tがアロケータを利用しない場合(std::uses_allocator_v<T, Alloc> == falseかつ、std::is_constructible_v<T, V1, V2, ..., VN> == trueならば)
obj(v1, v2, ..., vN);の形でコンストラクタを呼び出す。 -
Tがアロケータを利用し、アロケータをコンストラクタ引数の先頭で受け取る場合(std::uses_allocator_v<T, Alloc> == trueかつ、std::is_constructible_v<T,allocator_arg_t, Alloc, V1, V2, ..., VN> == trueならば)
obj(allocator_arg, alloc, v1, v2, ..., vN);の形でコンストラクタを呼び出す。 -
Tがアロケータを利用し、アロケータをコンストラクタ引数の末尾で受け取る場合(std::uses_allocator_v<T, Alloc> == trueかつ、std::is_constructible_v<T, V1, V2, ..., VN, Alloc> == trueならば)
obj(v1, v2, ..., vN, alloc);の形でコンストラクタを呼び出す。 -
それ以外の場合、要求された構築はill-formedである。
すなわち、構築したい型がアロケータを利用するならば引数とアロケータを適切な順序でコンストラクタに与えて呼び出し、利用しないならば通常のコンストラクタ呼び出しを行う仕組みである。
例えば、std::vector<std::string>のようにコンテナも要素型もアロケータを利用する場合に要素(std::string)とコンテナ(std::vector)でアロケータを共有させることを考える。この場合にコンテナに要素を追加するとき、コンテナ(std::vector)に設定されているアロケータを要素(std::string)のコンストラクタに引き渡す必要がある。
標準ライブラリ内でアロケータを利用するほとんどの型は、コンストラクタ引数列の末尾でアロケータオブジェクトを受け取る(上記std::stringやstd::vector等のコンテナ)。
しかし、一部の型ではコンストラクタ引数列の末尾で受け取るようにすると他のコンストラクタとあいまいになってしまうため、std::allocator_argと合わせてコンストラクタ引数列の先頭で受け取るようにしている。そのような型にはstd::tupleやstd::promise、std::packaged_taskがある。
これらのアロケータを利用する型及び多くのアロケータを必要としない型を構築する際、ユーザーから見た区別や追加の操作をする必要がないようにするために(あくまでライブラリ内部で解決するために)「アロケータを使用する構築」という仕組みが導入されている。
そしてこれによりpolymorphic_allocatorは、コンテナ等で利用されたときに必要ならばその内部要素型まで自然に伝搬する。ユーザーは意識してコンテナからアロケータを取得し要素型のコンストラクタに引き渡すようなコードを一切書く必要がなくなる。
本関数のstd::pair関連のオーバーロードは、std::pairが「アロケータを使用する構築」に対応したコンストラクタを持たないことから用意されている(コンストラクタの数が多くなるという理由で削除された経緯があるとのこと)。
- (1) :
*thisとargs...をコンストラクタ引数とした「アロケータを使用する構築」が可能であること。
アロケータを受け取るコンストラクタを持たない型については、(args..が適切ならば)この要件を常に満たしている。
C++17までは、この関数はTがstd::pairの特殊化でない場合に限りオーバーロード解決に参加する。
-
全て :
p--Tもしくはstd::pair<T1, T2>のオブジェクトを構築するメモリ領域へのポインタ -
(1) :
args--Tのコンストラクタに渡す引数列 -
(2) :
x--T1のコンストラクタに渡す引数を持つstd::tupleオブジェクトy--T2のコンストラクタに渡す引数を持つstd::tupleオブジェクト
-
(4) :
x--T1のコンストラクタに渡す引数y--T2のコンストラクタに渡す引数
-
(5) :
pr--T1, T2のコンストラクタにそれぞれコピーして渡す引数U, Vのオブジェクトを持つstd::pair<U, V>オブジェクト -
(6) :
pr--T1, T2のコンストラクタにそれぞれforwardして渡す引数U, Vのオブジェクトを持つstd::pair<U, V>オブジェクト
-
(1) :
Tのコンストラクタによって(「アロケータを使用する構築」によって)以下のいずれかと等価- アロケータを受け取らない場合 :
p = new(p) T(std::forward<Args>(args)...) - アロケータを引数の先頭で受け取る場合 :
p = new(p) T(allocator_arg, *this, std::forward<Args>(args)...) - アロケータを引数の末尾で受け取る場合 :
p = new(p) T(std::forward<Args>(args)..., *this)
- アロケータを受け取らない場合 :
-
(2) : 以下と等価
p = new(p) std::pair<T1, T2>(piecewise_construct_t, xprime, yprime)
xprimeはT1について(「アロケータを使用する構築」によって)以下のように定義する(yprimeはT2について同様に定義する)T1のコンストラクタがアロケータを受け取らない場合、xprimeはxT1のコンストラクタがアロケータを引数の先頭で受け取る場合、xprimeはstd::tuple_cat(std::make_tuple(allocator_arg, *this), x)T1のコンストラクタがアロケータを引数の末尾で受け取る場合、xprimeはstd::tuple_cat(x,std::make_tuple(*this))
-
(3) :
this->construct(p, std::piecewise_construct, std::tuple<>(), std::tuple<>());と等価、すなわち(2)に移譲 -
(4) : 以下と等価、すなわち(2)に移譲
this->construct(p, std::piecewise_construct,
std::forward_as_tuple(std::forward<U>(x)),
std::forward_as_tuple(std::forward<V>(y)));- (5) : 以下と等価、すなわち(2)に移譲
this->construct(p, std::piecewise_construct,
std::forward_as_tuple(pr.first),
std::forward_as_tuple(pr.second));- (6) : 以下と等価、すなわち(2)に移譲
this->construct(p, std::piecewise_construct,
std::forward_as_tuple(std::forward<U>(pr.first)),
std::forward_as_tuple(std::forward<V>(pr.second)));- (1) :
Tのコンストラクタが例外を投げないならば、この関数も例外を投げない。
#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);
}- construct[color ff0000]
- allocate[link allocate.md]
- deallocate[link deallocate.md]
- destroy[link destroy.md]
0
1
2
3
#include <iostream>
#include <memory_resource>
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;
}
}- construct[color ff0000]
- allocate[link allocate.md]
(2)
128
str
true
(3)
0
true
(4)
128
string
true
(5)
128
copy
true
(6)
128
move
true
- C++17
- Clang: ??
- GCC: 9.1
- Visual C++: 2017 update 6
- 2017, 2019共に(1)以外のオーバーロードを提供していない(ただし、(1)が全てを兼ねるC++20の実装がなされている)
- 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