libstdc++
debug/unordered_set
Go to the documentation of this file.
1 // Debugging unordered_set/unordered_multiset implementation -*- C++ -*-
2 
3 // Copyright (C) 2003-2022 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 /** @file debug/unordered_set
26  * This file is a GNU debug extension to the Standard C++ Library.
27  */
28 
29 #ifndef _GLIBCXX_DEBUG_UNORDERED_SET
30 #define _GLIBCXX_DEBUG_UNORDERED_SET 1
31 
32 #pragma GCC system_header
33 
34 #if __cplusplus < 201103L
35 # include <bits/c++0x_warning.h>
36 #else
37 # include <bits/c++config.h>
38 namespace std _GLIBCXX_VISIBILITY(default) { namespace __debug {
39  template<typename _Key, typename _Hash, typename _Pred, typename _Allocator>
40  class unordered_set;
41  template<typename _Key, typename _Hash, typename _Pred, typename _Allocator>
42  class unordered_multiset;
43 } } // namespace std::__debug
44 # include <unordered_set>
45 
46 #include <debug/safe_unordered_container.h>
47 #include <debug/safe_container.h>
48 #include <debug/safe_iterator.h>
49 #include <debug/safe_local_iterator.h>
50 
51 namespace std _GLIBCXX_VISIBILITY(default)
52 {
53 namespace __debug
54 {
55  /// Class std::unordered_set with safety/checking/debug instrumentation.
56  template<typename _Value,
57  typename _Hash = std::hash<_Value>,
58  typename _Pred = std::equal_to<_Value>,
59  typename _Alloc = std::allocator<_Value> >
60  class unordered_set
61  : public __gnu_debug::_Safe_container<
62  unordered_set<_Value, _Hash, _Pred, _Alloc>, _Alloc,
63  __gnu_debug::_Safe_unordered_container>,
64  public _GLIBCXX_STD_C::unordered_set<_Value, _Hash, _Pred, _Alloc>
65  {
66  typedef _GLIBCXX_STD_C::unordered_set<
67  _Value, _Hash, _Pred, _Alloc> _Base;
68  typedef __gnu_debug::_Safe_container<
69  unordered_set, _Alloc, __gnu_debug::_Safe_unordered_container> _Safe;
70 
71  typedef typename _Base::const_iterator _Base_const_iterator;
72  typedef typename _Base::iterator _Base_iterator;
73  typedef typename _Base::const_local_iterator _Base_const_local_iterator;
74  typedef typename _Base::local_iterator _Base_local_iterator;
75 
76  template<typename _ItT, typename _SeqT, typename _CatT>
77  friend class ::__gnu_debug::_Safe_iterator;
78  template<typename _ItT, typename _SeqT>
79  friend class ::__gnu_debug::_Safe_local_iterator;
80 
81  // Reference wrapper for base class. See PR libstdc++/90102.
82  struct _Base_ref
83  {
84  _Base_ref(const _Base& __r) : _M_ref(__r) { }
85 
86  const _Base& _M_ref;
87  };
88 
89  public:
90  typedef typename _Base::size_type size_type;
91  typedef typename _Base::difference_type difference_type;
92  typedef typename _Base::hasher hasher;
93  typedef typename _Base::key_equal key_equal;
94  typedef typename _Base::allocator_type allocator_type;
95 
96  typedef typename _Base::key_type key_type;
97  typedef typename _Base::value_type value_type;
98 
99  typedef typename _Base::pointer pointer;
100  typedef typename _Base::const_pointer const_pointer;
101  typedef typename _Base::reference reference;
102  typedef typename _Base::const_reference const_reference;
103  typedef __gnu_debug::_Safe_iterator<
104  _Base_iterator, unordered_set> iterator;
105  typedef __gnu_debug::_Safe_iterator<
106  _Base_const_iterator, unordered_set> const_iterator;
107  typedef __gnu_debug::_Safe_local_iterator<
108  _Base_local_iterator, unordered_set> local_iterator;
109  typedef __gnu_debug::_Safe_local_iterator<
110  _Base_const_local_iterator, unordered_set> const_local_iterator;
111 
112  unordered_set() = default;
113 
114  explicit
115  unordered_set(size_type __n,
116  const hasher& __hf = hasher(),
117  const key_equal& __eql = key_equal(),
118  const allocator_type& __a = allocator_type())
119  : _Base(__n, __hf, __eql, __a) { }
120 
121  template<typename _InputIterator>
122  unordered_set(_InputIterator __first, _InputIterator __last,
123  size_type __n = 0,
124  const hasher& __hf = hasher(),
125  const key_equal& __eql = key_equal(),
126  const allocator_type& __a = allocator_type())
127  : _Base(__gnu_debug::__base(
128  __glibcxx_check_valid_constructor_range(__first, __last)),
129  __gnu_debug::__base(__last), __n,
130  __hf, __eql, __a) { }
131 
132  unordered_set(const unordered_set&) = default;
133 
134  unordered_set(_Base_ref __x)
135  : _Base(__x._M_ref) { }
136 
137  unordered_set(unordered_set&&) = default;
138 
139  explicit
140  unordered_set(const allocator_type& __a)
141  : _Base(__a) { }
142 
143  unordered_set(const unordered_set& __uset,
144  const allocator_type& __a)
145  : _Base(__uset, __a) { }
146 
147  unordered_set(unordered_set&& __uset,
148  const allocator_type& __a)
149  noexcept( noexcept(_Base(std::move(__uset), __a)) )
150  : _Safe(std::move(__uset), __a),
151  _Base(std::move(__uset), __a) { }
152 
153  unordered_set(initializer_list<value_type> __l,
154  size_type __n = 0,
155  const hasher& __hf = hasher(),
156  const key_equal& __eql = key_equal(),
157  const allocator_type& __a = allocator_type())
158  : _Base(__l, __n, __hf, __eql, __a) { }
159 
160  unordered_set(size_type __n, const allocator_type& __a)
161  : unordered_set(__n, hasher(), key_equal(), __a)
162  { }
163 
164  unordered_set(size_type __n, const hasher& __hf,
165  const allocator_type& __a)
166  : unordered_set(__n, __hf, key_equal(), __a)
167  { }
168 
169  template<typename _InputIterator>
170  unordered_set(_InputIterator __first, _InputIterator __last,
171  size_type __n,
172  const allocator_type& __a)
173  : unordered_set(__first, __last, __n, hasher(), key_equal(), __a)
174  { }
175 
176  template<typename _InputIterator>
177  unordered_set(_InputIterator __first, _InputIterator __last,
178  size_type __n, const hasher& __hf,
179  const allocator_type& __a)
180  : unordered_set(__first, __last, __n, __hf, key_equal(), __a)
181  { }
182 
183  unordered_set(initializer_list<value_type> __l,
184  size_type __n,
185  const allocator_type& __a)
186  : unordered_set(__l, __n, hasher(), key_equal(), __a)
187  { }
188 
189  unordered_set(initializer_list<value_type> __l,
190  size_type __n, const hasher& __hf,
191  const allocator_type& __a)
192  : unordered_set(__l, __n, __hf, key_equal(), __a)
193  { }
194 
195  ~unordered_set() = default;
196 
197  unordered_set&
198  operator=(const unordered_set&) = default;
199 
200  unordered_set&
201  operator=(unordered_set&&) = default;
202 
203  unordered_set&
204  operator=(initializer_list<value_type> __l)
205  {
206  _Base::operator=(__l);
207  this->_M_invalidate_all();
208  return *this;
209  }
210 
211  using _Base::get_allocator;
212  using _Base::empty;
213  using _Base::size;
214  using _Base::max_size;
215 
216  void
217  swap(unordered_set& __x)
218  noexcept( noexcept(declval<_Base&>().swap(__x)) )
219  {
220  _Safe::_M_swap(__x);
221  _Base::swap(__x);
222  }
223 
224  void
225  clear() noexcept
226  {
227  _Base::clear();
228  this->_M_invalidate_all();
229  }
230 
231  iterator
232  begin() noexcept
233  { return { _Base::begin(), this }; }
234 
235  const_iterator
236  begin() const noexcept
237  { return { _Base::begin(), this }; }
238 
239  iterator
240  end() noexcept
241  { return { _Base::end(), this }; }
242 
243  const_iterator
244  end() const noexcept
245  { return { _Base::end(), this }; }
246 
247  const_iterator
248  cbegin() const noexcept
249  { return { _Base::cbegin(), this }; }
250 
251  const_iterator
252  cend() const noexcept
253  { return { _Base::cend(), this }; }
254 
255  // local versions
256  local_iterator
257  begin(size_type __b)
258  {
259  __glibcxx_check_bucket_index(__b);
260  return { _Base::begin(__b), this };
261  }
262 
263  local_iterator
264  end(size_type __b)
265  {
266  __glibcxx_check_bucket_index(__b);
267  return { _Base::end(__b), this };
268  }
269 
270  const_local_iterator
271  begin(size_type __b) const
272  {
273  __glibcxx_check_bucket_index(__b);
274  return { _Base::begin(__b), this };
275  }
276 
277  const_local_iterator
278  end(size_type __b) const
279  {
280  __glibcxx_check_bucket_index(__b);
281  return { _Base::end(__b), this };
282  }
283 
284  const_local_iterator
285  cbegin(size_type __b) const
286  {
287  __glibcxx_check_bucket_index(__b);
288  return { _Base::cbegin(__b), this };
289  }
290 
291  const_local_iterator
292  cend(size_type __b) const
293  {
294  __glibcxx_check_bucket_index(__b);
295  return { _Base::cend(__b), this };
296  }
297 
298  using _Base::bucket_count;
299  using _Base::max_bucket_count;
300 
301  size_type
302  bucket_size(size_type __b) const
303  {
304  __glibcxx_check_bucket_index(__b);
305  return _Base::bucket_size(__b);
306  }
307 
308  using _Base::bucket;
309  using _Base::load_factor;
310 
311  float
312  max_load_factor() const noexcept
313  { return _Base::max_load_factor(); }
314 
315  void
316  max_load_factor(float __f)
317  {
318  __glibcxx_check_max_load_factor(__f);
319  _Base::max_load_factor(__f);
320  }
321 
322  using _Base::rehash;
323  using _Base::reserve;
324 
325  template<typename... _Args>
326  std::pair<iterator, bool>
327  emplace(_Args&&... __args)
328  {
329  size_type __bucket_count = this->bucket_count();
330  auto __res = _Base::emplace(std::forward<_Args>(__args)...);
331  _M_check_rehashed(__bucket_count);
332  return { { __res.first, this }, __res.second };
333  }
334 
335  template<typename... _Args>
336  iterator
337  emplace_hint(const_iterator __hint, _Args&&... __args)
338  {
339  __glibcxx_check_insert(__hint);
340  size_type __bucket_count = this->bucket_count();
341  auto __it = _Base::emplace_hint(__hint.base(),
342  std::forward<_Args>(__args)...);
343  _M_check_rehashed(__bucket_count);
344  return { __it, this };
345  }
346 
347  std::pair<iterator, bool>
348  insert(const value_type& __obj)
349  {
350  size_type __bucket_count = this->bucket_count();
351  auto __res = _Base::insert(__obj);
352  _M_check_rehashed(__bucket_count);
353  return { { __res.first, this }, __res.second };
354  }
355 
356  iterator
357  insert(const_iterator __hint, const value_type& __obj)
358  {
359  __glibcxx_check_insert(__hint);
360  size_type __bucket_count = this->bucket_count();
361  auto __it = _Base::insert(__hint.base(), __obj);
362  _M_check_rehashed(__bucket_count);
363  return { __it, this };
364  }
365 
366  std::pair<iterator, bool>
367  insert(value_type&& __obj)
368  {
369  size_type __bucket_count = this->bucket_count();
370  auto __res = _Base::insert(std::move(__obj));
371  _M_check_rehashed(__bucket_count);
372  return { { __res.first, this }, __res.second };
373  }
374 
375  iterator
376  insert(const_iterator __hint, value_type&& __obj)
377  {
378  __glibcxx_check_insert(__hint);
379  size_type __bucket_count = this->bucket_count();
380  auto __it = _Base::insert(__hint.base(), std::move(__obj));
381  _M_check_rehashed(__bucket_count);
382  return { __it, this };
383  }
384 
385  void
386  insert(std::initializer_list<value_type> __l)
387  {
388  size_type __bucket_count = this->bucket_count();
389  _Base::insert(__l);
390  _M_check_rehashed(__bucket_count);
391  }
392 
393  template<typename _InputIterator>
394  void
395  insert(_InputIterator __first, _InputIterator __last)
396  {
397  typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
398  __glibcxx_check_valid_range2(__first, __last, __dist);
399  size_type __bucket_count = this->bucket_count();
400 
401  if (__dist.second >= __gnu_debug::__dp_sign)
402  _Base::insert(__gnu_debug::__unsafe(__first),
403  __gnu_debug::__unsafe(__last));
404  else
405  _Base::insert(__first, __last);
406 
407  _M_check_rehashed(__bucket_count);
408  }
409 
410 #if __cplusplus > 201402L
411  using node_type = typename _Base::node_type;
412  using insert_return_type = _Node_insert_return<iterator, node_type>;
413 
414  node_type
415  extract(const_iterator __position)
416  {
417  __glibcxx_check_erase(__position);
418  return _M_extract(__position.base());
419  }
420 
421  node_type
422  extract(const key_type& __key)
423  {
424  const auto __position = _Base::find(__key);
425  if (__position != _Base::end())
426  return _M_extract(__position);
427  return {};
428  }
429 
430  insert_return_type
431  insert(node_type&& __nh)
432  {
433  auto __ret = _Base::insert(std::move(__nh));
434  return
435  { { __ret.position, this }, __ret.inserted, std::move(__ret.node) };
436  }
437 
438  iterator
439  insert(const_iterator __hint, node_type&& __nh)
440  {
441  __glibcxx_check_insert(__hint);
442  return { _Base::insert(__hint.base(), std::move(__nh)), this };
443  }
444 
445  template<typename _H2, typename _P2>
446  void
447  merge(unordered_set<_Value, _H2, _P2, _Alloc>& __source)
448  {
449  auto __guard
450  = _Safe::_S_uc_guard(std::__detail::_Identity{}, __source);
451  _Base::merge(__source);
452  }
453 
454  template<typename _H2, typename _P2>
455  void
456  merge(unordered_set<_Value, _H2, _P2, _Alloc>&& __source)
457  { merge(__source); }
458 
459  template<typename _H2, typename _P2>
460  void
461  merge(unordered_multiset<_Value, _H2, _P2, _Alloc>& __source)
462  {
463  auto __guard
464  = _Safe::_S_umc_guard(std::__detail::_Identity{}, __source);
465  _Base::merge(__source);
466  }
467 
468  template<typename _H2, typename _P2>
469  void
470  merge(unordered_multiset<_Value, _H2, _P2, _Alloc>&& __source)
471  { merge(__source); }
472 #endif // C++17
473 
474  using _Base::hash_function;
475  using _Base::key_eq;
476 
477  iterator
478  find(const key_type& __key)
479  { return { _Base::find(__key), this }; }
480 
481 #if __cplusplus > 201703L
482  template<typename _Kt,
483  typename = std::__has_is_transparent_t<_Hash, _Kt>,
484  typename = std::__has_is_transparent_t<_Pred, _Kt>>
485  iterator
486  find(const _Kt& __k)
487  { return { _Base::find(__k), this }; }
488 #endif
489 
490  const_iterator
491  find(const key_type& __key) const
492  { return { _Base::find(__key), this }; }
493 
494 #if __cplusplus > 201703L
495  template<typename _Kt,
496  typename = std::__has_is_transparent_t<_Hash, _Kt>,
497  typename = std::__has_is_transparent_t<_Pred, _Kt>>
498  const_iterator
499  find(const _Kt& __k) const
500  { return { _Base::find(__k), this }; }
501 #endif
502 
503  using _Base::count;
504 
505 #if __cplusplus > 201703L
506  using _Base::contains;
507 #endif
508 
509  std::pair<iterator, iterator>
510  equal_range(const key_type& __key)
511  {
512  auto __res = _Base::equal_range(__key);
513  return { { __res.first, this }, { __res.second, this } };
514  }
515 
516 #if __cplusplus > 201703L
517  template<typename _Kt,
518  typename = std::__has_is_transparent_t<_Hash, _Kt>,
519  typename = std::__has_is_transparent_t<_Pred, _Kt>>
520  std::pair<iterator, iterator>
521  equal_range(const _Kt& __k)
522  {
523  auto __res = _Base::equal_range(__k);
524  return { { __res.first, this }, { __res.second, this } };
525  }
526 #endif
527 
528  std::pair<const_iterator, const_iterator>
529  equal_range(const key_type& __key) const
530  {
531  auto __res = _Base::equal_range(__key);
532  return { { __res.first, this }, { __res.second, this } };
533  }
534 
535 #if __cplusplus > 201703L
536  template<typename _Kt,
537  typename = std::__has_is_transparent_t<_Hash, _Kt>,
538  typename = std::__has_is_transparent_t<_Pred, _Kt>>
539  std::pair<const_iterator, const_iterator>
540  equal_range(const _Kt& __k) const
541  {
542  auto __res = _Base::equal_range(__k);
543  return { { __res.first, this }, { __res.second, this } };
544  }
545 #endif
546 
547  size_type
548  erase(const key_type& __key)
549  {
550  size_type __ret(0);
551  auto __victim = _Base::find(__key);
552  if (__victim != _Base::end())
553  {
554  _M_erase(__victim);
555  __ret = 1;
556  }
557  return __ret;
558  }
559 
560  iterator
561  erase(const_iterator __it)
562  {
563  __glibcxx_check_erase(__it);
564  return { _M_erase(__it.base()), this };
565  }
566 
567  _Base_iterator
568  erase(_Base_const_iterator __it)
569  {
570  __glibcxx_check_erase2(__it);
571  return _M_erase(__it);
572  }
573 
574  iterator
575  erase(iterator __it)
576  {
577  __glibcxx_check_erase(__it);
578  return { _M_erase(__it.base()), this };
579  }
580 
581  iterator
582  erase(const_iterator __first, const_iterator __last)
583  {
584  __glibcxx_check_erase_range(__first, __last);
585  for (auto __tmp = __first.base(); __tmp != __last.base(); ++__tmp)
586  {
587  _GLIBCXX_DEBUG_VERIFY(__tmp != _Base::cend(),
588  _M_message(__gnu_debug::__msg_valid_range)
589  ._M_iterator(__first, "first")
590  ._M_iterator(__last, "last"));
591  _M_invalidate(__tmp);
592  }
593 
594  size_type __bucket_count = this->bucket_count();
595  auto __next = _Base::erase(__first.base(), __last.base());
596  _M_check_rehashed(__bucket_count);
597  return { __next, this };
598  }
599 
600  _Base&
601  _M_base() noexcept { return *this; }
602 
603  const _Base&
604  _M_base() const noexcept { return *this; }
605 
606  private:
607  void
608  _M_check_rehashed(size_type __prev_count)
609  {
610  if (__prev_count != this->bucket_count())
611  this->_M_invalidate_all();
612  }
613 
614  void
615  _M_invalidate(_Base_const_iterator __victim)
616  {
617  this->_M_invalidate_if(
618  [__victim](_Base_const_iterator __it) { return __it == __victim; });
619  this->_M_invalidate_local_if(
620  [__victim](_Base_const_local_iterator __it)
621  { return __it == __victim; });
622  }
623 
624  _Base_iterator
625  _M_erase(_Base_const_iterator __victim)
626  {
627  _M_invalidate(__victim);
628  size_type __bucket_count = this->bucket_count();
629  _Base_iterator __next = _Base::erase(__victim);
630  _M_check_rehashed(__bucket_count);
631  return __next;
632  }
633 
634 #if __cplusplus > 201402L
635  node_type
636  _M_extract(_Base_const_iterator __victim)
637  {
638  _M_invalidate(__victim);
639  return _Base::extract(__victim);
640  }
641 #endif
642  };
643 
644 #if __cpp_deduction_guides >= 201606
645 
646  template<typename _InputIterator,
647  typename _Hash =
648  hash<typename iterator_traits<_InputIterator>::value_type>,
649  typename _Pred =
650  equal_to<typename iterator_traits<_InputIterator>::value_type>,
651  typename _Allocator =
652  allocator<typename iterator_traits<_InputIterator>::value_type>,
653  typename = _RequireInputIter<_InputIterator>,
654  typename = _RequireNotAllocatorOrIntegral<_Hash>,
655  typename = _RequireNotAllocator<_Pred>,
656  typename = _RequireAllocator<_Allocator>>
657  unordered_set(_InputIterator, _InputIterator,
658  unordered_set<int>::size_type = {},
659  _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
660  -> unordered_set<typename iterator_traits<_InputIterator>::value_type,
661  _Hash, _Pred, _Allocator>;
662 
663  template<typename _Tp, typename _Hash = hash<_Tp>,
664  typename _Pred = equal_to<_Tp>,
665  typename _Allocator = allocator<_Tp>,
666  typename = _RequireNotAllocatorOrIntegral<_Hash>,
667  typename = _RequireNotAllocator<_Pred>,
668  typename = _RequireAllocator<_Allocator>>
669  unordered_set(initializer_list<_Tp>,
670  unordered_set<int>::size_type = {},
671  _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
672  -> unordered_set<_Tp, _Hash, _Pred, _Allocator>;
673 
674  template<typename _InputIterator, typename _Allocator,
675  typename = _RequireInputIter<_InputIterator>,
676  typename = _RequireAllocator<_Allocator>>
677  unordered_set(_InputIterator, _InputIterator,
678  unordered_set<int>::size_type, _Allocator)
679  -> unordered_set<typename iterator_traits<_InputIterator>::value_type,
680  hash<
681  typename iterator_traits<_InputIterator>::value_type>,
682  equal_to<
683  typename iterator_traits<_InputIterator>::value_type>,
684  _Allocator>;
685 
686  template<typename _InputIterator, typename _Hash, typename _Allocator,
687  typename = _RequireInputIter<_InputIterator>,
688  typename = _RequireNotAllocatorOrIntegral<_Hash>,
689  typename = _RequireAllocator<_Allocator>>
690  unordered_set(_InputIterator, _InputIterator,
691  unordered_set<int>::size_type,
692  _Hash, _Allocator)
693  -> unordered_set<typename iterator_traits<_InputIterator>::value_type,
694  _Hash,
695  equal_to<
696  typename iterator_traits<_InputIterator>::value_type>,
697  _Allocator>;
698 
699  template<typename _Tp, typename _Allocator,
700  typename = _RequireAllocator<_Allocator>>
701  unordered_set(initializer_list<_Tp>,
702  unordered_set<int>::size_type, _Allocator)
703  -> unordered_set<_Tp, hash<_Tp>, equal_to<_Tp>, _Allocator>;
704 
705  template<typename _Tp, typename _Hash, typename _Allocator,
706  typename = _RequireNotAllocatorOrIntegral<_Hash>,
707  typename = _RequireAllocator<_Allocator>>
708  unordered_set(initializer_list<_Tp>,
709  unordered_set<int>::size_type, _Hash, _Allocator)
710  -> unordered_set<_Tp, _Hash, equal_to<_Tp>, _Allocator>;
711 
712 #endif
713 
714  template<typename _Value, typename _Hash, typename _Pred, typename _Alloc>
715  inline void
716  swap(unordered_set<_Value, _Hash, _Pred, _Alloc>& __x,
717  unordered_set<_Value, _Hash, _Pred, _Alloc>& __y)
718  noexcept(noexcept(__x.swap(__y)))
719  { __x.swap(__y); }
720 
721  template<typename _Value, typename _Hash, typename _Pred, typename _Alloc>
722  inline bool
723  operator==(const unordered_set<_Value, _Hash, _Pred, _Alloc>& __x,
724  const unordered_set<_Value, _Hash, _Pred, _Alloc>& __y)
725  { return __x._M_base() == __y._M_base(); }
726 
727 #if __cpp_impl_three_way_comparison < 201907L
728  template<typename _Value, typename _Hash, typename _Pred, typename _Alloc>
729  inline bool
730  operator!=(const unordered_set<_Value, _Hash, _Pred, _Alloc>& __x,
731  const unordered_set<_Value, _Hash, _Pred, _Alloc>& __y)
732  { return !(__x == __y); }
733 #endif
734 
735  /// Class std::unordered_multiset with safety/checking/debug instrumentation.
736  template<typename _Value,
737  typename _Hash = std::hash<_Value>,
738  typename _Pred = std::equal_to<_Value>,
739  typename _Alloc = std::allocator<_Value> >
740  class unordered_multiset
741  : public __gnu_debug::_Safe_container<
742  unordered_multiset<_Value, _Hash, _Pred, _Alloc>, _Alloc,
743  __gnu_debug::_Safe_unordered_container>,
744  public _GLIBCXX_STD_C::unordered_multiset<_Value, _Hash, _Pred, _Alloc>
745  {
746  typedef _GLIBCXX_STD_C::unordered_multiset<
747  _Value, _Hash, _Pred, _Alloc> _Base;
748  typedef __gnu_debug::_Safe_container<unordered_multiset,
749  _Alloc, __gnu_debug::_Safe_unordered_container> _Safe;
750  typedef typename _Base::const_iterator _Base_const_iterator;
751  typedef typename _Base::iterator _Base_iterator;
752  typedef typename _Base::const_local_iterator
753  _Base_const_local_iterator;
754  typedef typename _Base::local_iterator _Base_local_iterator;
755 
756  template<typename _ItT, typename _SeqT, typename _CatT>
757  friend class ::__gnu_debug::_Safe_iterator;
758  template<typename _ItT, typename _SeqT>
759  friend class ::__gnu_debug::_Safe_local_iterator;
760 
761  // Reference wrapper for base class. See PR libstdc++/90102.
762  struct _Base_ref
763  {
764  _Base_ref(const _Base& __r) : _M_ref(__r) { }
765 
766  const _Base& _M_ref;
767  };
768 
769  public:
770  typedef typename _Base::size_type size_type;
771  typedef typename _Base::difference_type difference_type;
772  typedef typename _Base::hasher hasher;
773  typedef typename _Base::key_equal key_equal;
774  typedef typename _Base::allocator_type allocator_type;
775 
776  typedef typename _Base::key_type key_type;
777  typedef typename _Base::value_type value_type;
778 
779  typedef typename _Base::pointer pointer;
780  typedef typename _Base::const_pointer const_pointer;
781  typedef typename _Base::reference reference;
782  typedef typename _Base::const_reference const_reference;
783  typedef __gnu_debug::_Safe_iterator<
784  _Base_iterator, unordered_multiset> iterator;
785  typedef __gnu_debug::_Safe_iterator<
786  _Base_const_iterator, unordered_multiset> const_iterator;
787  typedef __gnu_debug::_Safe_local_iterator<
788  _Base_local_iterator, unordered_multiset> local_iterator;
789  typedef __gnu_debug::_Safe_local_iterator<
790  _Base_const_local_iterator, unordered_multiset> const_local_iterator;
791 
792  unordered_multiset() = default;
793 
794  explicit
795  unordered_multiset(size_type __n,
796  const hasher& __hf = hasher(),
797  const key_equal& __eql = key_equal(),
798  const allocator_type& __a = allocator_type())
799  : _Base(__n, __hf, __eql, __a) { }
800 
801  template<typename _InputIterator>
802  unordered_multiset(_InputIterator __first, _InputIterator __last,
803  size_type __n = 0,
804  const hasher& __hf = hasher(),
805  const key_equal& __eql = key_equal(),
806  const allocator_type& __a = allocator_type())
807  : _Base(__gnu_debug::__base(
808  __glibcxx_check_valid_constructor_range(__first, __last)),
809  __gnu_debug::__base(__last), __n,
810  __hf, __eql, __a) { }
811 
812  unordered_multiset(const unordered_multiset&) = default;
813 
814  unordered_multiset(_Base_ref __x)
815  : _Base(__x._M_ref) { }
816 
817  unordered_multiset(unordered_multiset&&) = default;
818 
819  explicit
820  unordered_multiset(const allocator_type& __a)
821  : _Base(__a) { }
822 
823  unordered_multiset(const unordered_multiset& __uset,
824  const allocator_type& __a)
825  : _Base(__uset, __a) { }
826 
827  unordered_multiset(unordered_multiset&& __uset,
828  const allocator_type& __a)
829  noexcept( noexcept(_Base(std::move(__uset), __a)) )
830  : _Safe(std::move(__uset), __a),
831  _Base(std::move(__uset), __a) { }
832 
833  unordered_multiset(initializer_list<value_type> __l,
834  size_type __n = 0,
835  const hasher& __hf = hasher(),
836  const key_equal& __eql = key_equal(),
837  const allocator_type& __a = allocator_type())
838  : _Base(__l, __n, __hf, __eql, __a) { }
839 
840  unordered_multiset(size_type __n, const allocator_type& __a)
841  : unordered_multiset(__n, hasher(), key_equal(), __a)
842  { }
843 
844  unordered_multiset(size_type __n, const hasher& __hf,
845  const allocator_type& __a)
846  : unordered_multiset(__n, __hf, key_equal(), __a)
847  { }
848 
849  template<typename _InputIterator>
850  unordered_multiset(_InputIterator __first, _InputIterator __last,
851  size_type __n,
852  const allocator_type& __a)
853  : unordered_multiset(__first, __last, __n, hasher(), key_equal(), __a)
854  { }
855 
856  template<typename _InputIterator>
857  unordered_multiset(_InputIterator __first, _InputIterator __last,
858  size_type __n, const hasher& __hf,
859  const allocator_type& __a)
860  : unordered_multiset(__first, __last, __n, __hf, key_equal(), __a)
861  { }
862 
863  unordered_multiset(initializer_list<value_type> __l,
864  size_type __n,
865  const allocator_type& __a)
866  : unordered_multiset(__l, __n, hasher(), key_equal(), __a)
867  { }
868 
869  unordered_multiset(initializer_list<value_type> __l,
870  size_type __n, const hasher& __hf,
871  const allocator_type& __a)
872  : unordered_multiset(__l, __n, __hf, key_equal(), __a)
873  { }
874 
875  ~unordered_multiset() = default;
876 
877  unordered_multiset&
878  operator=(const unordered_multiset&) = default;
879 
880  unordered_multiset&
881  operator=(unordered_multiset&&) = default;
882 
883  unordered_multiset&
884  operator=(initializer_list<value_type> __l)
885  {
886  _Base::operator=(__l);
887  this->_M_invalidate_all();
888  return *this;
889  }
890 
891  using _Base::get_allocator;
892  using _Base::empty;
893  using _Base::size;
894  using _Base::max_size;
895 
896  void
897  swap(unordered_multiset& __x)
898  noexcept( noexcept(declval<_Base&>().swap(__x)) )
899  {
900  _Safe::_M_swap(__x);
901  _Base::swap(__x);
902  }
903 
904  void
905  clear() noexcept
906  {
907  _Base::clear();
908  this->_M_invalidate_all();
909  }
910 
911  iterator
912  begin() noexcept
913  { return { _Base::begin(), this }; }
914 
915  const_iterator
916  begin() const noexcept
917  { return { _Base::begin(), this }; }
918 
919  iterator
920  end() noexcept
921  { return { _Base::end(), this }; }
922 
923  const_iterator
924  end() const noexcept
925  { return { _Base::end(), this }; }
926 
927  const_iterator
928  cbegin() const noexcept
929  { return { _Base::cbegin(), this }; }
930 
931  const_iterator
932  cend() const noexcept
933  { return { _Base::cend(), this }; }
934 
935  // local versions
936  local_iterator
937  begin(size_type __b)
938  {
939  __glibcxx_check_bucket_index(__b);
940  return { _Base::begin(__b), this };
941  }
942 
943  local_iterator
944  end(size_type __b)
945  {
946  __glibcxx_check_bucket_index(__b);
947  return { _Base::end(__b), this };
948  }
949 
950  const_local_iterator
951  begin(size_type __b) const
952  {
953  __glibcxx_check_bucket_index(__b);
954  return { _Base::begin(__b), this };
955  }
956 
957  const_local_iterator
958  end(size_type __b) const
959  {
960  __glibcxx_check_bucket_index(__b);
961  return { _Base::end(__b), this };
962  }
963 
964  const_local_iterator
965  cbegin(size_type __b) const
966  {
967  __glibcxx_check_bucket_index(__b);
968  return { _Base::cbegin(__b), this };
969  }
970 
971  const_local_iterator
972  cend(size_type __b) const
973  {
974  __glibcxx_check_bucket_index(__b);
975  return { _Base::cend(__b), this };
976  }
977 
978  using _Base::bucket_count;
979  using _Base::max_bucket_count;
980 
981  size_type
982  bucket_size(size_type __b) const
983  {
984  __glibcxx_check_bucket_index(__b);
985  return _Base::bucket_size(__b);
986  }
987 
988  using _Base::bucket;
989  using _Base::load_factor;
990 
991  float
992  max_load_factor() const noexcept
993  { return _Base::max_load_factor(); }
994 
995  void
996  max_load_factor(float __f)
997  {
998  __glibcxx_check_max_load_factor(__f);
999  _Base::max_load_factor(__f);
1000  }
1001 
1002  using _Base::rehash;
1003  using _Base::reserve;
1004 
1005  template<typename... _Args>
1006  iterator
1007  emplace(_Args&&... __args)
1008  {
1009  size_type __bucket_count = this->bucket_count();
1010  auto __it = _Base::emplace(std::forward<_Args>(__args)...);
1011  _M_check_rehashed(__bucket_count);
1012  return { __it, this };
1013  }
1014 
1015  template<typename... _Args>
1016  iterator
1017  emplace_hint(const_iterator __hint, _Args&&... __args)
1018  {
1019  __glibcxx_check_insert(__hint);
1020  size_type __bucket_count = this->bucket_count();
1021  auto __it = _Base::emplace_hint(__hint.base(),
1022  std::forward<_Args>(__args)...);
1023  _M_check_rehashed(__bucket_count);
1024  return { __it, this };
1025  }
1026 
1027  iterator
1028  insert(const value_type& __obj)
1029  {
1030  size_type __bucket_count = this->bucket_count();
1031  auto __it = _Base::insert(__obj);
1032  _M_check_rehashed(__bucket_count);
1033  return { __it, this };
1034  }
1035 
1036  iterator
1037  insert(const_iterator __hint, const value_type& __obj)
1038  {
1039  __glibcxx_check_insert(__hint);
1040  size_type __bucket_count = this->bucket_count();
1041  auto __it = _Base::insert(__hint.base(), __obj);
1042  _M_check_rehashed(__bucket_count);
1043  return { __it, this };
1044  }
1045 
1046  iterator
1047  insert(value_type&& __obj)
1048  {
1049  size_type __bucket_count = this->bucket_count();
1050  auto __it = _Base::insert(std::move(__obj));
1051  _M_check_rehashed(__bucket_count);
1052  return { __it, this };
1053  }
1054 
1055  iterator
1056  insert(const_iterator __hint, value_type&& __obj)
1057  {
1058  __glibcxx_check_insert(__hint);
1059  size_type __bucket_count = this->bucket_count();
1060  auto __it = _Base::insert(__hint.base(), std::move(__obj));
1061  _M_check_rehashed(__bucket_count);
1062  return { __it, this };
1063  }
1064 
1065  void
1066  insert(std::initializer_list<value_type> __l)
1067  {
1068  size_type __bucket_count = this->bucket_count();
1069  _Base::insert(__l);
1070  _M_check_rehashed(__bucket_count);
1071  }
1072 
1073  template<typename _InputIterator>
1074  void
1075  insert(_InputIterator __first, _InputIterator __last)
1076  {
1077  typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
1078  __glibcxx_check_valid_range2(__first, __last, __dist);
1079  size_type __bucket_count = this->bucket_count();
1080 
1081  if (__dist.second >= __gnu_debug::__dp_sign)
1082  _Base::insert(__gnu_debug::__unsafe(__first),
1083  __gnu_debug::__unsafe(__last));
1084  else
1085  _Base::insert(__first, __last);
1086 
1087  _M_check_rehashed(__bucket_count);
1088  }
1089 
1090 #if __cplusplus > 201402L
1091  using node_type = typename _Base::node_type;
1092 
1093  node_type
1094  extract(const_iterator __position)
1095  {
1096  __glibcxx_check_erase(__position);
1097  return _M_extract(__position.base());
1098  }
1099 
1100  node_type
1101  extract(const key_type& __key)
1102  {
1103  const auto __position = _Base::find(__key);
1104  if (__position != _Base::end())
1105  return _M_extract(__position);
1106  return {};
1107  }
1108 
1109  iterator
1110  insert(node_type&& __nh)
1111  { return { _Base::insert(std::move(__nh)), this }; }
1112 
1113  iterator
1114  insert(const_iterator __hint, node_type&& __nh)
1115  {
1116  __glibcxx_check_insert(__hint);
1117  return { _Base::insert(__hint.base(), std::move(__nh)), this };
1118  }
1119 
1120  template<typename _H2, typename _P2>
1121  void
1122  merge(unordered_multiset<_Value, _H2, _P2, _Alloc>& __source)
1123  {
1124  auto __guard
1125  = _Safe::_S_umc_guard(std::__detail::_Identity{}, __source);
1126  _Base::merge(__source);
1127  }
1128 
1129  template<typename _H2, typename _P2>
1130  void
1131  merge(unordered_multiset<_Value, _H2, _P2, _Alloc>&& __source)
1132  { merge(__source); }
1133 
1134  template<typename _H2, typename _P2>
1135  void
1136  merge(unordered_set<_Value, _H2, _P2, _Alloc>& __source)
1137  {
1138  auto __guard
1139  = _Safe::_S_uc_guard(std::__detail::_Identity{}, __source);
1140  _Base::merge(__source);
1141  }
1142 
1143  template<typename _H2, typename _P2>
1144  void
1145  merge(unordered_set<_Value, _H2, _P2, _Alloc>&& __source)
1146  { merge(__source); }
1147 #endif // C++17
1148 
1149  using _Base::hash_function;
1150  using _Base::key_eq;
1151 
1152  iterator
1153  find(const key_type& __key)
1154  { return { _Base::find(__key), this }; }
1155 
1156 #if __cplusplus > 201703L
1157  template<typename _Kt,
1158  typename = std::__has_is_transparent_t<_Hash, _Kt>,
1159  typename = std::__has_is_transparent_t<_Pred, _Kt>>
1160  iterator
1161  find(const _Kt& __k)
1162  { return { _Base::find(__k), this }; }
1163 #endif
1164 
1165  const_iterator
1166  find(const key_type& __key) const
1167  { return { _Base::find(__key), this }; }
1168 
1169 #if __cplusplus > 201703L
1170  template<typename _Kt,
1171  typename = std::__has_is_transparent_t<_Hash, _Kt>,
1172  typename = std::__has_is_transparent_t<_Pred, _Kt>>
1173  const_iterator
1174  find(const _Kt& __k) const
1175  { return { _Base::find(__k), this }; }
1176 #endif
1177 
1178  using _Base::count;
1179 
1180 #if __cplusplus > 201703L
1181  using _Base::contains;
1182 #endif
1183 
1184  std::pair<iterator, iterator>
1185  equal_range(const key_type& __key)
1186  {
1187  auto __res = _Base::equal_range(__key);
1188  return { { __res.first, this }, { __res.second, this } };
1189  }
1190 
1191 #if __cplusplus > 201703L
1192  template<typename _Kt,
1193  typename = std::__has_is_transparent_t<_Hash, _Kt>,
1194  typename = std::__has_is_transparent_t<_Pred, _Kt>>
1195  std::pair<iterator, iterator>
1196  equal_range(const _Kt& __k)
1197  {
1198  auto __res = _Base::equal_range(__k);
1199  return { { __res.first, this }, { __res.second, this } };
1200  }
1201 #endif
1202 
1203  std::pair<const_iterator, const_iterator>
1204  equal_range(const key_type& __key) const
1205  {
1206  auto __res = _Base::equal_range(__key);
1207  return { { __res.first, this }, { __res.second, this } };
1208  }
1209 
1210 #if __cplusplus > 201703L
1211  template<typename _Kt,
1212  typename = std::__has_is_transparent_t<_Hash, _Kt>,
1213  typename = std::__has_is_transparent_t<_Pred, _Kt>>
1214  std::pair<const_iterator, const_iterator>
1215  equal_range(const _Kt& __k) const
1216  {
1217  auto __res = _Base::equal_range(__k);
1218  return { { __res.first, this }, { __res.second, this } };
1219  }
1220 #endif
1221 
1222  size_type
1223  erase(const key_type& __key)
1224  {
1225  size_type __ret(0);
1226  auto __pair = _Base::equal_range(__key);
1227  for (auto __victim = __pair.first; __victim != __pair.second;)
1228  {
1229  _M_invalidate(__victim);
1230  __victim = _Base::erase(__victim);
1231  ++__ret;
1232  }
1233 
1234  return __ret;
1235  }
1236 
1237  iterator
1238  erase(const_iterator __it)
1239  {
1240  __glibcxx_check_erase(__it);
1241  return { _M_erase(__it.base()), this };
1242  }
1243 
1244  _Base_iterator
1245  erase(_Base_const_iterator __it)
1246  {
1247  __glibcxx_check_erase2(__it);
1248  return _M_erase(__it);
1249  }
1250 
1251  iterator
1252  erase(iterator __it)
1253  {
1254  __glibcxx_check_erase(__it);
1255  return { _M_erase(__it.base()), this };
1256  }
1257 
1258  iterator
1259  erase(const_iterator __first, const_iterator __last)
1260  {
1261  __glibcxx_check_erase_range(__first, __last);
1262  for (auto __tmp = __first.base(); __tmp != __last.base(); ++__tmp)
1263  {
1264  _GLIBCXX_DEBUG_VERIFY(__tmp != _Base::cend(),
1265  _M_message(__gnu_debug::__msg_valid_range)
1266  ._M_iterator(__first, "first")
1267  ._M_iterator(__last, "last"));
1268  _M_invalidate(__tmp);
1269  }
1270  return { _Base::erase(__first.base(), __last.base()), this };
1271  }
1272 
1273  _Base&
1274  _M_base() noexcept { return *this; }
1275 
1276  const _Base&
1277  _M_base() const noexcept { return *this; }
1278 
1279  private:
1280  void
1281  _M_check_rehashed(size_type __prev_count)
1282  {
1283  if (__prev_count != this->bucket_count())
1284  this->_M_invalidate_all();
1285  }
1286 
1287  void
1288  _M_invalidate(_Base_const_iterator __victim)
1289  {
1290  this->_M_invalidate_if(
1291  [__victim](_Base_const_iterator __it) { return __it == __victim; });
1292  this->_M_invalidate_local_if(
1293  [__victim](_Base_const_local_iterator __it)
1294  { return __it == __victim; });
1295  }
1296 
1297  _Base_iterator
1298  _M_erase(_Base_const_iterator __victim)
1299  {
1300  _M_invalidate(__victim);
1301  size_type __bucket_count = this->bucket_count();
1302  _Base_iterator __next = _Base::erase(__victim);
1303  _M_check_rehashed(__bucket_count);
1304  return __next;
1305  }
1306 
1307 #if __cplusplus > 201402L
1308  node_type
1309  _M_extract(_Base_const_iterator __victim)
1310  {
1311  _M_invalidate(__victim);
1312  return _Base::extract(__victim);
1313  }
1314 #endif
1315  };
1316 
1317 #if __cpp_deduction_guides >= 201606
1318 
1319  template<typename _InputIterator,
1320  typename _Hash =
1321  hash<typename iterator_traits<_InputIterator>::value_type>,
1322  typename _Pred =
1323  equal_to<typename iterator_traits<_InputIterator>::value_type>,
1324  typename _Allocator =
1325  allocator<typename iterator_traits<_InputIterator>::value_type>,
1326  typename = _RequireInputIter<_InputIterator>,
1327  typename = _RequireNotAllocatorOrIntegral<_Hash>,
1328  typename = _RequireNotAllocator<_Pred>,
1329  typename = _RequireAllocator<_Allocator>>
1330  unordered_multiset(_InputIterator, _InputIterator,
1331  unordered_multiset<int>::size_type = {},
1332  _Hash = _Hash(), _Pred = _Pred(),
1333  _Allocator = _Allocator())
1334  -> unordered_multiset<typename iterator_traits<_InputIterator>::value_type,
1335  _Hash, _Pred, _Allocator>;
1336 
1337  template<typename _Tp, typename _Hash = hash<_Tp>,
1338  typename _Pred = equal_to<_Tp>,
1339  typename _Allocator = allocator<_Tp>,
1340  typename = _RequireNotAllocatorOrIntegral<_Hash>,
1341  typename = _RequireNotAllocator<_Pred>,
1342  typename = _RequireAllocator<_Allocator>>
1343  unordered_multiset(initializer_list<_Tp>,
1344  unordered_multiset<int>::size_type = {},
1345  _Hash = _Hash(), _Pred = _Pred(),
1346  _Allocator = _Allocator())
1347  -> unordered_multiset<_Tp, _Hash, _Pred, _Allocator>;
1348 
1349  template<typename _InputIterator, typename _Allocator,
1350  typename = _RequireInputIter<_InputIterator>,
1351  typename = _RequireAllocator<_Allocator>>
1352  unordered_multiset(_InputIterator, _InputIterator,
1353  unordered_multiset<int>::size_type, _Allocator)
1354  -> unordered_multiset<typename iterator_traits<_InputIterator>::value_type,
1355  hash<typename
1356  iterator_traits<_InputIterator>::value_type>,
1357  equal_to<typename
1358  iterator_traits<_InputIterator>::value_type>,
1359  _Allocator>;
1360 
1361  template<typename _InputIterator, typename _Hash, typename _Allocator,
1362  typename = _RequireInputIter<_InputIterator>,
1363  typename = _RequireNotAllocatorOrIntegral<_Hash>,
1364  typename = _RequireAllocator<_Allocator>>
1365  unordered_multiset(_InputIterator, _InputIterator,
1366  unordered_multiset<int>::size_type,
1367  _Hash, _Allocator)
1368  -> unordered_multiset<typename
1369  iterator_traits<_InputIterator>::value_type,
1370  _Hash,
1371  equal_to<
1372  typename
1373  iterator_traits<_InputIterator>::value_type>,
1374  _Allocator>;
1375 
1376  template<typename _Tp, typename _Allocator,
1377  typename = _RequireAllocator<_Allocator>>
1378  unordered_multiset(initializer_list<_Tp>,
1379  unordered_multiset<int>::size_type, _Allocator)
1380  -> unordered_multiset<_Tp, hash<_Tp>, equal_to<_Tp>, _Allocator>;
1381 
1382  template<typename _Tp, typename _Hash, typename _Allocator,
1383  typename = _RequireNotAllocatorOrIntegral<_Hash>,
1384  typename = _RequireAllocator<_Allocator>>
1385  unordered_multiset(initializer_list<_Tp>,
1386  unordered_multiset<int>::size_type, _Hash, _Allocator)
1387  -> unordered_multiset<_Tp, _Hash, equal_to<_Tp>, _Allocator>;
1388 
1389 #endif
1390 
1391  template<typename _Value, typename _Hash, typename _Pred, typename _Alloc>
1392  inline void
1393  swap(unordered_multiset<_Value, _Hash, _Pred, _Alloc>& __x,
1394  unordered_multiset<_Value, _Hash, _Pred, _Alloc>& __y)
1395  noexcept(noexcept(__x.swap(__y)))
1396  { __x.swap(__y); }
1397 
1398  template<typename _Value, typename _Hash, typename _Pred, typename _Alloc>
1399  inline bool
1400  operator==(const unordered_multiset<_Value, _Hash, _Pred, _Alloc>& __x,
1401  const unordered_multiset<_Value, _Hash, _Pred, _Alloc>& __y)
1402  { return __x._M_base() == __y._M_base(); }
1403 
1404 #if __cpp_impl_three_way_comparison < 201907L
1405  template<typename _Value, typename _Hash, typename _Pred, typename _Alloc>
1406  inline bool
1407  operator!=(const unordered_multiset<_Value, _Hash, _Pred, _Alloc>& __x,
1408  const unordered_multiset<_Value, _Hash, _Pred, _Alloc>& __y)
1409  { return !(__x == __y); }
1410 #endif
1411 
1412 } // namespace __debug
1413 } // namespace std
1414 
1415 #endif // C++11
1416 
1417 #endif