memory_resource には std::pair に対しての処理が入っていなかったですね。
おんや? これで良いんだべ?
とか疑問に思った読者もいるでしょう。
はい、std::pair 引数の受付は memory_resource ではなく polymorphic_allocator によって、実装されています。
この 2 つのヘッダーが良くも悪くも、今の実装状況ということになるかと思います。
uses_allocator.h (https://github.com/MacKomatsu/gcc/blob/master/libstdc%2B%2B-v3/include/bits/uses_allocator.h).
55 template<typename _Tp, typename _Alloc, typename = __void_t<>>
56 struct __uses_allocator_helper
57 : false_type { };
58
59 template<typename _Tp, typename _Alloc>
60 struct __uses_allocator_helper<_Tp, _Alloc,
61 __void_t<typename _Tp::allocator_type>>
62 : __is_erased_or_convertible<_Alloc, typename _Tp::allocator_type>::type
63 { };
64
65 /// [allocator.uses.trait]
66 template<typename _Tp, typename _Alloc>
67 struct uses_allocator
68 : __uses_allocator_helper<_Tp, _Alloc>::type
69 { };
70
71 struct __uses_alloc_base { };
72
73 struct __uses_alloc0 : __uses_alloc_base
74 {
75 struct _Sink { void operator=(const void*) { } } _M_a;
76 };
77
78 template<typename _Alloc>
79 struct __uses_alloc1 : __uses_alloc_base { const _Alloc* _M_a; };
80
81 template<typename _Alloc>
82 struct __uses_alloc2 : __uses_alloc_base { const _Alloc* _M_a; };
83
84 template<bool, typename _Tp, typename _Alloc, typename... _Args>
85 struct __uses_alloc;
86
87 template<typename _Tp, typename _Alloc, typename... _Args>
88 struct __uses_alloc<true, _Tp, _Alloc, _Args...>
89 : conditional<
90 is_constructible<_Tp, allocator_arg_t, _Alloc, _Args...>::value,
91 __uses_alloc1<_Alloc>,
92 __uses_alloc2<_Alloc>>::type
93 {
94 static_assert(__or_<
95 is_constructible<_Tp, allocator_arg_t, _Alloc, _Args...>,
96 is_constructible<_Tp, _Args..., _Alloc>>::value, "construction with"
97 " an allocator must be possible if uses_allocator is true");
98 };
99
100 template<typename _Tp, typename _Alloc, typename... _Args>
101 struct __uses_alloc<false, _Tp, _Alloc, _Args...>
102 : __uses_alloc0 { };
103
104 template<typename _Tp, typename _Alloc, typename... _Args>
105 using __uses_alloc_t =
106 __uses_alloc<uses_allocator<_Tp, _Alloc>::value, _Tp, _Alloc, _Args...>;
107
108 template<typename _Tp, typename _Alloc, typename... _Args>
109 inline __uses_alloc_t<_Tp, _Alloc, _Args...>
110 __use_alloc(const _Alloc& __a)
111 {
112 __uses_alloc_t<_Tp, _Alloc, _Args...> __ret;
113 __ret._M_a = std::__addressof(__a);
114 return __ret;
115 }
ポリモーフィックアロケーターのソースコードは以下の通りです。
polymorphic_allocator クラス (https://github.com/MacKomatsu/gcc/blob/master/libstdc%2B%2B-v3/include/experimental/memory_resource).
115 // 8.6 Class template polymorphic_allocator
116 template <class _Tp>
117 class polymorphic_allocator
118 {
119 using __uses_alloc1_ = __uses_alloc1<memory_resource*>;
120 using __uses_alloc2_ = __uses_alloc2<memory_resource*>;
121
122 template<typename _Tp1, typename... _Args>
123 void
124 _M_construct(__uses_alloc0, _Tp1* __p, _Args&&... __args)
125 { ::new(__p) _Tp1(std::forward<_Args>(__args)...); }
126
127 template<typename _Tp1, typename... _Args>
128 void
129 _M_construct(__uses_alloc1_, _Tp1* __p, _Args&&... __args)
130 { ::new(__p) _Tp1(allocator_arg, this->resource(),
131 std::forward<_Args>(__args)...); }
132
133 template<typename _Tp1, typename... _Args>
134 void
135 _M_construct(__uses_alloc2_, _Tp1* __p, _Args&&... __args)
136 { ::new(__p) _Tp1(std::forward<_Args>(__args)...,
137 this->resource()); }
138
139 public:
140 using value_type = _Tp;
141
142 polymorphic_allocator() noexcept
143 : _M_resource(get_default_resource())
144 { }
145
146 polymorphic_allocator(memory_resource* __r)
147 : _M_resource(__r)
148 { _GLIBCXX_DEBUG_ASSERT(__r); }
149
150 polymorphic_allocator(const polymorphic_allocator& __other) = default;
151
152 template <typename _Up>
153 polymorphic_allocator(const polymorphic_allocator<_Up>&
154 __other) noexcept
155 : _M_resource(__other.resource())
156 { }
157
158 polymorphic_allocator&
159 operator=(const polymorphic_allocator& __rhs) = default;
160
161 _Tp* allocate(size_t __n)
162 { return static_cast<_Tp*>(_M_resource->allocate(__n * sizeof(_Tp),
163 alignof(_Tp))); }
164
165 void deallocate(_Tp* __p, size_t __n)
166 { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); }
167
168 template <typename _Tp1, typename... _Args> //used here
169 void construct(_Tp1* __p, _Args&&... __args)
170 {
171 memory_resource* const __resource = this->resource();
172 auto __use_tag
173 = __use_alloc<_Tp1, memory_resource*, _Args...>(__resource);
174 _M_construct(__use_tag, __p, std::forward<_Args>(__args)...);
175 }
176
177 // Specializations for pair using piecewise construction
178 template <typename _Tp1, typename _Tp2,
179 typename... _Args1, typename... _Args2>
180 void construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t,
181 tuple<_Args1...> __x,
182 tuple<_Args2...> __y)
183 {
184 memory_resource* const __resource = this->resource();
185 auto __x_use_tag =
186 __use_alloc<_Tp1, memory_resource*, _Args1...>(__resource);
187 auto __y_use_tag =
188 __use_alloc<_Tp2, memory_resource*, _Args2...>(__resource);
189
190 ::new(__p) std::pair<_Tp1, _Tp2>(piecewise_construct,
191 _M_construct_p(__x_use_tag, __x),
192 _M_construct_p(__y_use_tag, __y));
193 }
194
195 template <typename _Tp1, typename _Tp2>
196 void construct(pair<_Tp1,_Tp2>* __p)
197 { this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
198
199 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
200 void construct(pair<_Tp1,_Tp2>* __p, _Up&& __x, _Vp&& __y)
201 { this->construct(__p, piecewise_construct,
202 forward_as_tuple(std::forward<_Up>(__x)),
203 forward_as_tuple(std::forward<_Vp>(__y))); }
204
205 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
206 void construct(pair<_Tp1,_Tp2>* __p, const std::pair<_Up, _Vp>& __pr)
207 { this->construct(__p, piecewise_construct, forward_as_tuple(__pr.first),
208 forward_as_tuple(__pr.second)); }
209
210 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
211 void construct(pair<_Tp1,_Tp2>* __p, pair<_Up, _Vp>&& __pr)
212 { this->construct(__p, piecewise_construct,
213 forward_as_tuple(std::forward<_Up>(__pr.first)),
214 forward_as_tuple(std::forward<_Vp>(__pr.second))); }
215
216 template <typename _Up>
217 void destroy(_Up* __p)
218 { __p->~_Up(); }
219
220 // Return a default-constructed allocator (no allocator propagation)
221 polymorphic_allocator select_on_container_copy_construction() const
222 { return polymorphic_allocator(); }
223
224 memory_resource* resource() const
225 { return _M_resource; }
226
227 private:
228 template<typename _Tuple>
229 _Tuple&&
230 _M_construct_p(__uses_alloc0, _Tuple& __t)
231 { return std::move(__t); }
232
233 template<typename... _Args>
234 decltype(auto)
235 _M_construct_p(__uses_alloc1_ __ua, tuple<_Args...>& __t)
236 { return tuple_cat(make_tuple(allocator_arg, *(__ua._M_a)),
237 std::move(__t)); }
238
239 template<typename... _Args>
240 decltype(auto)
241 _M_construct_p(__uses_alloc2_ __ua, tuple<_Args...>& __t)
242 { return tuple_cat(std::move(__t), make_tuple(*(__ua._M_a))); }
243
244 memory_resource* _M_resource;
245 };
はーいはい、これまでの本書の蓄積があれば、ソースの分かりみが深いはずです。
ですよね?
std::pair 対策もきっちりやっていますし、筆者のサンプルコードと違って非の打ち所の無い完璧なコードです。
他の pmr コンテナについては以下のような定義となると思いますが 2019 年の段階で使えるかは、まだ試していないので分かりませんね。
namespace pmr {
template <class T>
using list = std::list<T, std::pmr::polymorphic_allocator<T>>;
}
namespace pmr {
template <class Key, class T, class Compare = std::less<Key>>
using map = std::map<Key, T, Compare,
std::pmr::polymorphic_allocator<std::pair<const Key,T>>>
}
namespace pmr {
template <class Key, class Compare = std::less<Key>>
using multiset = std::multiset<Key, Compare,
std::pmr::polymorphic_allocator<Key>>;
}
namespace pmr {
template <class Key, class Compare = std::less<Key>>
using set = std::set<Key, Compare, std::pmr::polymorphic_allocator<Key>>;
}
namespace pmr {
template <class Key,
class T,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_map = std::unordered_map<Key, T, Hash, Pred,
std::pmr::polymorphic_allocator<std::pair<const Key,T>>>;
}
namespace pmr {
template <class Key, class T,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred,
std::pmr::polymorphic_allocator<std::pair<const Key,T>>>;
}
namespace pmr {
template <class Key,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_set = std::unordered_set<Key, Hash, Pred,
std::pmr::polymorphic_allocator<Key>>;
}
namespace pmr {
template <class Key,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>>
using unordered_multiset = std::unordered_multiset<Key, Hash, Pred,
std::pmr::polymorphic_allocator<Key>>
}まだ実験的なものという理解なんで、これを使用しているコードを見る機会はあまり無いと思います。
Copyright 2018-2019, by Masaki Komatsu