Skip to content

Commit aca28f1

Browse files
authored
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558)
This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
1 parent 1f58cbe commit aca28f1

File tree

14 files changed

+202
-132
lines changed

14 files changed

+202
-132
lines changed

libcxx/include/__tree

Lines changed: 95 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,18 @@ public:
887887
}
888888

889889
_LIBCPP_HIDE_FROM_ABI __tree(const __tree& __t);
890+
891+
_LIBCPP_HIDE_FROM_ABI __tree(const __tree& __other, const allocator_type& __alloc)
892+
: __begin_node_(__end_node()), __node_alloc_(__alloc), __size_(0), __value_comp_(__other.value_comp()) {
893+
if (__other.size() == 0)
894+
return;
895+
896+
*__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__other.__root()));
897+
__root()->__parent_ = __end_node();
898+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
899+
__size_ = __other.size();
900+
}
901+
890902
_LIBCPP_HIDE_FROM_ABI __tree& operator=(const __tree& __t);
891903
template <class _ForwardIterator>
892904
_LIBCPP_HIDE_FROM_ABI void __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
@@ -995,27 +1007,6 @@ public:
9951007
std::forward<_Args>(__args)...);
9961008
}
9971009

998-
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
999-
_LIBCPP_HIDE_FROM_ABI void
1000-
__insert_unique_from_orphaned_node(const_iterator __p, __get_node_value_type_t<_Tp>&& __value) {
1001-
__emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
1002-
}
1003-
1004-
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
1005-
_LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
1006-
__emplace_hint_unique(__p, std::move(__value));
1007-
}
1008-
1009-
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
1010-
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
1011-
__emplace_hint_multi(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
1012-
}
1013-
1014-
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
1015-
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
1016-
__emplace_hint_multi(__p, std::move(__value));
1017-
}
1018-
10191010
template <class _InIter, class _Sent>
10201011
_LIBCPP_HIDE_FROM_ABI void __insert_range_multi(_InIter __first, _Sent __last) {
10211012
if (__first == __last)
@@ -1388,19 +1379,19 @@ private:
13881379
// copy the exact structure 1:1. Since this is for copy construction _only_ we know that we get a correct tree. If we
13891380
// didn't get a correct tree, the invariants of __tree are broken and we have a much bigger problem than an improperly
13901381
// balanced tree.
1382+
template <class _NodeConstructor>
13911383
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
13921384
_LIBCPP_HIDE_FROM_ABI
13931385
#endif
1394-
__node_pointer
1395-
__copy_construct_tree(__node_pointer __src) {
1386+
__node_pointer __construct_from_tree(__node_pointer __src, _NodeConstructor __construct) {
13961387
if (!__src)
13971388
return nullptr;
13981389

1399-
__node_holder __new_node = __construct_node(__src->__get_value());
1390+
__node_holder __new_node = __construct(__src->__get_value());
14001391

14011392
unique_ptr<__node, __tree_deleter> __left(
1402-
__copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
1403-
__node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
1393+
__construct_from_tree(static_cast<__node_pointer>(__src->__left_), __construct), __node_alloc_);
1394+
__node_pointer __right = __construct_from_tree(static_cast<__node_pointer>(__src->__right_), __construct);
14041395

14051396
__node_pointer __new_node_ptr = __new_node.release();
14061397

@@ -1414,46 +1405,85 @@ private:
14141405
return __new_node_ptr;
14151406
}
14161407

1408+
_LIBCPP_HIDE_FROM_ABI __node_pointer __copy_construct_tree(__node_pointer __src) {
1409+
return __construct_from_tree(__src, [this](const value_type& __val) { return __construct_node(__val); });
1410+
}
1411+
1412+
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
1413+
_LIBCPP_HIDE_FROM_ABI __node_pointer __move_construct_tree(__node_pointer __src) {
1414+
return __construct_from_tree(__src, [this](value_type& __val) {
1415+
return __construct_node(const_cast<key_type&&>(__val.first), std::move(__val.second));
1416+
});
1417+
}
1418+
1419+
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
1420+
_LIBCPP_HIDE_FROM_ABI __node_pointer __move_construct_tree(__node_pointer __src) {
1421+
return __construct_from_tree(__src, [this](value_type& __val) { return __construct_node(std::move(__val)); });
1422+
}
1423+
1424+
template <class _Assignment, class _ConstructionAlg>
14171425
// This copy assignment will always produce a correct red-black-tree assuming the incoming tree is correct, since our
14181426
// own tree is a red-black-tree and the incoming tree is a red-black-tree. The invariants of a red-black-tree are
14191427
// temporarily not met until all of the incoming red-black tree is copied.
14201428
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
14211429
_LIBCPP_HIDE_FROM_ABI
14221430
#endif
1423-
__node_pointer
1424-
__copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
1431+
__node_pointer __assign_from_tree(
1432+
__node_pointer __dest, __node_pointer __src, _Assignment __assign, _ConstructionAlg __construct_subtree) {
14251433
if (!__src) {
14261434
destroy(__dest);
14271435
return nullptr;
14281436
}
14291437

1430-
__assign_value(__dest->__get_value(), __src->__get_value());
1438+
__assign(__dest->__get_value(), __src->__get_value());
14311439
__dest->__is_black_ = __src->__is_black_;
14321440

14331441
// If we already have a left node in the destination tree, reuse it and copy-assign recursively
14341442
if (__dest->__left_) {
1435-
__dest->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
1436-
static_cast<__node_pointer>(__dest->__left_), static_cast<__node_pointer>(__src->__left_)));
1443+
__dest->__left_ = static_cast<__node_base_pointer>(__assign_from_tree(
1444+
static_cast<__node_pointer>(__dest->__left_),
1445+
static_cast<__node_pointer>(__src->__left_),
1446+
__assign,
1447+
__construct_subtree));
14371448

14381449
// Otherwise, we must create new nodes; copy-construct from here on
14391450
} else if (__src->__left_) {
1440-
auto __new_left = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
1451+
auto __new_left = __construct_subtree(static_cast<__node_pointer>(__src->__left_));
14411452
__dest->__left_ = static_cast<__node_base_pointer>(__new_left);
14421453
__new_left->__parent_ = static_cast<__end_node_pointer>(__dest);
14431454
}
14441455

14451456
// Identical to the left case above, just for the right nodes
14461457
if (__dest->__right_) {
1447-
__dest->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
1448-
static_cast<__node_pointer>(__dest->__right_), static_cast<__node_pointer>(__src->__right_)));
1458+
__dest->__right_ = static_cast<__node_base_pointer>(__assign_from_tree(
1459+
static_cast<__node_pointer>(__dest->__right_),
1460+
static_cast<__node_pointer>(__src->__right_),
1461+
__assign,
1462+
__construct_subtree));
14491463
} else if (__src->__right_) {
1450-
auto __new_right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
1464+
auto __new_right = __construct_subtree(static_cast<__node_pointer>(__src->__right_));
14511465
__dest->__right_ = static_cast<__node_base_pointer>(__new_right);
14521466
__new_right->__parent_ = static_cast<__end_node_pointer>(__dest);
14531467
}
14541468

14551469
return __dest;
14561470
}
1471+
1472+
_LIBCPP_HIDE_FROM_ABI __node_pointer __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
1473+
return __assign_from_tree(
1474+
__dest,
1475+
__src,
1476+
[](value_type& __lhs, const value_type& __rhs) { __assign_value(__lhs, __rhs); },
1477+
[this](__node_pointer __nd) { return __copy_construct_tree(__nd); });
1478+
}
1479+
1480+
_LIBCPP_HIDE_FROM_ABI __node_pointer __move_assign_tree(__node_pointer __dest, __node_pointer __src) {
1481+
return __assign_from_tree(
1482+
__dest,
1483+
__src,
1484+
[](value_type& __lhs, value_type& __rhs) { __assign_value(__lhs, std::move(__rhs)); },
1485+
[this](__node_pointer __nd) { return __move_construct_tree(__nd); });
1486+
}
14571487
};
14581488

14591489
// Precondition: __size_ != 0
@@ -1594,21 +1624,26 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
15941624

15951625
template <class _Tp, class _Compare, class _Allocator>
15961626
__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
1597-
: __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(std::move(__t.value_comp())) {
1627+
: __begin_node_(__end_node()),
1628+
__node_alloc_(__node_allocator(__a)),
1629+
__size_(0),
1630+
__value_comp_(std::move(__t.value_comp())) {
1631+
if (__t.size() == 0)
1632+
return;
15981633
if (__a == __t.__alloc()) {
1599-
if (__t.__size_ == 0)
1600-
__begin_node_ = __end_node();
1601-
else {
1602-
__begin_node_ = __t.__begin_node_;
1603-
__end_node()->__left_ = __t.__end_node()->__left_;
1604-
__end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
1605-
__size_ = __t.__size_;
1606-
__t.__begin_node_ = __t.__end_node();
1607-
__t.__end_node()->__left_ = nullptr;
1608-
__t.__size_ = 0;
1609-
}
1634+
__begin_node_ = __t.__begin_node_;
1635+
__end_node()->__left_ = __t.__end_node()->__left_;
1636+
__end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
1637+
__size_ = __t.__size_;
1638+
__t.__begin_node_ = __t.__end_node();
1639+
__t.__end_node()->__left_ = nullptr;
1640+
__t.__size_ = 0;
16101641
} else {
1611-
__begin_node_ = __end_node();
1642+
*__root_ptr() = static_cast<__node_base_pointer>(__move_construct_tree(__t.__root()));
1643+
__root()->__parent_ = __end_node();
1644+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
1645+
__size_ = __t.size();
1646+
__t.clear(); // Ensure that __t is in a valid state after moving out the keys
16121647
}
16131648
}
16141649

@@ -1633,22 +1668,21 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
16331668

16341669
template <class _Tp, class _Compare, class _Allocator>
16351670
void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
1636-
if (__node_alloc() == __t.__node_alloc())
1671+
if (__node_alloc() == __t.__node_alloc()) {
16371672
__move_assign(__t, true_type());
1638-
else {
1639-
value_comp() = std::move(__t.value_comp());
1640-
const_iterator __e = end();
1673+
} else {
1674+
value_comp() = std::move(__t.value_comp());
16411675
if (__size_ != 0) {
1642-
_DetachedTreeCache __cache(this);
1643-
while (__cache.__get() != nullptr && __t.__size_ != 0) {
1644-
__assign_value(__cache.__get()->__get_value(), std::move(__t.remove(__t.begin())->__get_value()));
1645-
__node_insert_multi(__cache.__get());
1646-
__cache.__advance();
1647-
}
1648-
}
1649-
while (__t.__size_ != 0) {
1650-
__insert_multi_from_orphaned_node(__e, std::move(__t.remove(__t.begin())->__get_value()));
1676+
*__root_ptr() = static_cast<__node_base_pointer>(__move_assign_tree(__root(), __t.__root()));
1677+
} else {
1678+
*__root_ptr() = static_cast<__node_base_pointer>(__move_construct_tree(__t.__root()));
1679+
if (__root())
1680+
__root()->__parent_ = __end_node();
16511681
}
1682+
__begin_node_ =
1683+
__end_node()->__left_ ? static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_)) : __end_node();
1684+
__size_ = __t.size();
1685+
__t.clear(); // Ensure that __t is in a valid state after moving out the keys
16521686
}
16531687
}
16541688

libcxx/include/map

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ public:
995995

996996
_LIBCPP_HIDE_FROM_ABI map(map&& __m) = default;
997997

998-
_LIBCPP_HIDE_FROM_ABI map(map&& __m, const allocator_type& __a);
998+
_LIBCPP_HIDE_FROM_ABI map(map&& __m, const allocator_type& __a) : __tree_(std::move(__m.__tree_), __a) {}
999999

10001000
_LIBCPP_HIDE_FROM_ABI map& operator=(map&& __m) = default;
10011001

@@ -1023,10 +1023,7 @@ public:
10231023

10241024
_LIBCPP_HIDE_FROM_ABI explicit map(const allocator_type& __a) : __tree_(typename __base::allocator_type(__a)) {}
10251025

1026-
_LIBCPP_HIDE_FROM_ABI map(const map& __m, const allocator_type& __a)
1027-
: __tree_(__m.__tree_.value_comp(), typename __base::allocator_type(__a)) {
1028-
insert(__m.begin(), __m.end());
1029-
}
1026+
_LIBCPP_HIDE_FROM_ABI map(const map& __m, const allocator_type& __alloc) : __tree_(__m.__tree_, __alloc) {}
10301027

10311028
_LIBCPP_HIDE_FROM_ABI ~map() { static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), ""); }
10321029

@@ -1426,18 +1423,6 @@ map(initializer_list<pair<_Key, _Tp>>, _Allocator)
14261423
# endif
14271424

14281425
# ifndef _LIBCPP_CXX03_LANG
1429-
template <class _Key, class _Tp, class _Compare, class _Allocator>
1430-
map<_Key, _Tp, _Compare, _Allocator>::map(map&& __m, const allocator_type& __a)
1431-
: __tree_(std::move(__m.__tree_), typename __base::allocator_type(__a)) {
1432-
if (__a != __m.get_allocator()) {
1433-
const_iterator __e = cend();
1434-
while (!__m.empty()) {
1435-
__tree_.__insert_unique_from_orphaned_node(
1436-
__e.__i_, std::move(__m.__tree_.remove(__m.begin().__i_)->__get_value()));
1437-
}
1438-
}
1439-
}
1440-
14411426
template <class _Key, class _Tp, class _Compare, class _Allocator>
14421427
_Tp& map<_Key, _Tp, _Compare, _Allocator>::operator[](const key_type& __k) {
14431428
return __tree_.__emplace_unique(std::piecewise_construct, std::forward_as_tuple(__k), std::forward_as_tuple())
@@ -1683,7 +1668,7 @@ public:
16831668

16841669
_LIBCPP_HIDE_FROM_ABI multimap(multimap&& __m) = default;
16851670

1686-
_LIBCPP_HIDE_FROM_ABI multimap(multimap&& __m, const allocator_type& __a);
1671+
_LIBCPP_HIDE_FROM_ABI multimap(multimap&& __m, const allocator_type& __a) : __tree_(std::move(__m.__tree_), __a) {}
16871672

16881673
_LIBCPP_HIDE_FROM_ABI multimap& operator=(multimap&& __m) = default;
16891674

@@ -1712,10 +1697,7 @@ public:
17121697

17131698
_LIBCPP_HIDE_FROM_ABI explicit multimap(const allocator_type& __a) : __tree_(typename __base::allocator_type(__a)) {}
17141699

1715-
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m, const allocator_type& __a)
1716-
: __tree_(__m.__tree_.value_comp(), typename __base::allocator_type(__a)) {
1717-
insert(__m.begin(), __m.end());
1718-
}
1700+
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m, const allocator_type& __a) : __tree_(__m.__tree_, __a) {}
17191701

17201702
_LIBCPP_HIDE_FROM_ABI ~multimap() {
17211703
static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
@@ -1990,19 +1972,6 @@ multimap(initializer_list<pair<_Key, _Tp>>, _Allocator)
19901972
-> multimap<remove_const_t<_Key>, _Tp, less<remove_const_t<_Key>>, _Allocator>;
19911973
# endif
19921974

1993-
# ifndef _LIBCPP_CXX03_LANG
1994-
template <class _Key, class _Tp, class _Compare, class _Allocator>
1995-
multimap<_Key, _Tp, _Compare, _Allocator>::multimap(multimap&& __m, const allocator_type& __a)
1996-
: __tree_(std::move(__m.__tree_), typename __base::allocator_type(__a)) {
1997-
if (__a != __m.get_allocator()) {
1998-
const_iterator __e = cend();
1999-
while (!__m.empty())
2000-
__tree_.__insert_multi_from_orphaned_node(
2001-
__e.__i_, std::move(__m.__tree_.remove(__m.begin().__i_)->__get_value()));
2002-
}
2003-
}
2004-
# endif
2005-
20061975
template <class _Key, class _Tp, class _Compare, class _Allocator>
20071976
inline _LIBCPP_HIDE_FROM_ABI bool
20081977
operator==(const multimap<_Key, _Tp, _Compare, _Allocator>& __x, const multimap<_Key, _Tp, _Compare, _Allocator>& __y) {

libcxx/include/set

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -671,12 +671,10 @@ public:
671671

672672
_LIBCPP_HIDE_FROM_ABI explicit set(const allocator_type& __a) : __tree_(__a) {}
673673

674-
_LIBCPP_HIDE_FROM_ABI set(const set& __s, const allocator_type& __a) : __tree_(__s.__tree_.value_comp(), __a) {
675-
insert(__s.begin(), __s.end());
676-
}
674+
_LIBCPP_HIDE_FROM_ABI set(const set& __s, const allocator_type& __alloc) : __tree_(__s.__tree_, __alloc) {}
677675

678676
# ifndef _LIBCPP_CXX03_LANG
679-
_LIBCPP_HIDE_FROM_ABI set(set&& __s, const allocator_type& __a);
677+
_LIBCPP_HIDE_FROM_ABI set(set&& __s, const allocator_type& __alloc) : __tree_(std::move(__s.__tree_), __alloc) {}
680678

681679
_LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
682680
: __tree_(__comp) {
@@ -946,19 +944,6 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
946944
set(initializer_list<_Key>, _Allocator) -> set<_Key, less<_Key>, _Allocator>;
947945
# endif
948946

949-
# ifndef _LIBCPP_CXX03_LANG
950-
951-
template <class _Key, class _Compare, class _Allocator>
952-
set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {
953-
if (__a != __s.get_allocator()) {
954-
const_iterator __e = cend();
955-
while (!__s.empty())
956-
insert(__e, std::move(__s.__tree_.remove(__s.begin())->__get_value()));
957-
}
958-
}
959-
960-
# endif // _LIBCPP_CXX03_LANG
961-
962947
template <class _Key, class _Compare, class _Allocator>
963948
inline _LIBCPP_HIDE_FROM_ABI bool
964949
operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
@@ -1128,13 +1113,10 @@ public:
11281113
# ifndef _LIBCPP_CXX03_LANG
11291114
_LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s) = default;
11301115

1131-
_LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a);
1116+
_LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {}
11321117
# endif // _LIBCPP_CXX03_LANG
11331118
_LIBCPP_HIDE_FROM_ABI explicit multiset(const allocator_type& __a) : __tree_(__a) {}
1134-
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a)
1135-
: __tree_(__s.__tree_.value_comp(), __a) {
1136-
insert(__s.begin(), __s.end());
1137-
}
1119+
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a) : __tree_(__s.__tree_, __a) {}
11381120

11391121
# ifndef _LIBCPP_CXX03_LANG
11401122
_LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
@@ -1407,20 +1389,6 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
14071389
multiset(initializer_list<_Key>, _Allocator) -> multiset<_Key, less<_Key>, _Allocator>;
14081390
# endif
14091391

1410-
# ifndef _LIBCPP_CXX03_LANG
1411-
1412-
template <class _Key, class _Compare, class _Allocator>
1413-
multiset<_Key, _Compare, _Allocator>::multiset(multiset&& __s, const allocator_type& __a)
1414-
: __tree_(std::move(__s.__tree_), __a) {
1415-
if (__a != __s.get_allocator()) {
1416-
const_iterator __e = cend();
1417-
while (!__s.empty())
1418-
insert(__e, std::move(__s.__tree_.remove(__s.begin())->__get_value()));
1419-
}
1420-
}
1421-
1422-
# endif // _LIBCPP_CXX03_LANG
1423-
14241392
template <class _Key, class _Compare, class _Allocator>
14251393
inline _LIBCPP_HIDE_FROM_ABI bool
14261394
operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {

0 commit comments

Comments
 (0)