109.1. ポリモーフィックアロケーターの実装

 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