[libc++] Optimize copy construction and assignment of __tree (#151304)

```
----------------------------------------------------------------------------------------------------------
Benchmark                                                                              old             new
----------------------------------------------------------------------------------------------------------
std::map<int, int>::ctor(const&)/0                                                 15.5 ns         14.9 ns
std::map<int, int>::ctor(const&)/32                                                 474 ns          321 ns
std::map<int, int>::ctor(const&)/1024                                             24591 ns        11101 ns
std::map<int, int>::ctor(const&)/8192                                            236153 ns        98868 ns
std::map<std::string, int>::ctor(const&)/0                                         15.2 ns         14.9 ns
std::map<std::string, int>::ctor(const&)/32                                        2673 ns         2340 ns
std::map<std::string, int>::ctor(const&)/1024                                    115354 ns        86088 ns
std::map<std::string, int>::ctor(const&)/8192                                   1298510 ns       626876 ns
std::map<int, int>::operator=(const&) (into cleared Container)/0                   16.5 ns         16.1 ns
std::map<int, int>::operator=(const&) (into cleared Container)/32                   548 ns          323 ns
std::map<int, int>::operator=(const&) (into cleared Container)/1024               28418 ns        11026 ns
std::map<int, int>::operator=(const&) (into cleared Container)/8192              281827 ns        97113 ns
std::map<int, int>::operator=(const&) (into populated Container)/0                 2.42 ns         1.85 ns
std::map<int, int>::operator=(const&) (into populated Container)/32                 369 ns         73.0 ns
std::map<int, int>::operator=(const&) (into populated Container)/1024             24078 ns         2322 ns
std::map<int, int>::operator=(const&) (into populated Container)/8192            266537 ns        22963 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/0           16.6 ns         16.2 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/32          2614 ns         1622 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/1024      116826 ns        63281 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/8192     1316655 ns       649177 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/0         2.42 ns         1.89 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/32        1264 ns          581 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/1024    238826 ns        39943 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/8192   2412327 ns       379456 ns
```

Fixes #77658
Fixes #62571
This commit is contained in:
Nikolas Klauser
2025-08-05 09:49:40 +02:00
committed by GitHub
parent 091c33b1f5
commit 1cac2be874
22 changed files with 2004 additions and 1049 deletions

View File

@@ -43,6 +43,9 @@ Implemented Papers
Improvements and New Features
-----------------------------
- The performance of ``map::map(const map&)`` has been improved up to 2.3x
- The performance of ``map::operator=(const map&)`` has been improved by up to 11x
Deprecations and Removals
-------------------------

View File

@@ -1213,6 +1213,104 @@ private:
__node_pointer __cache_root_;
__node_pointer __cache_elem_;
};
class __tree_deleter {
__node_allocator& __alloc_;
public:
using pointer = __node_pointer;
_LIBCPP_HIDE_FROM_ABI __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
_LIBCPP_HIDE_FROM_ABI
#endif
void
operator()(__node_pointer __ptr) {
if (!__ptr)
return;
(*this)(static_cast<__node_pointer>(__ptr->__left_));
auto __right = __ptr->__right_;
__node_traits::destroy(__alloc_, std::addressof(__ptr->__value_));
__node_traits::deallocate(__alloc_, __ptr, 1);
(*this)(static_cast<__node_pointer>(__right));
}
};
// This copy construction will always produce a correct red-black-tree assuming the incoming tree is correct, since we
// copy the exact structure 1:1. Since this is for copy construction _only_ we know that we get a correct tree. If we
// didn't get a correct tree, the invariants of __tree are broken and we have a much bigger problem than an improperly
// balanced tree.
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
_LIBCPP_HIDE_FROM_ABI
#endif
__node_pointer
__copy_construct_tree(__node_pointer __src) {
if (!__src)
return nullptr;
__node_holder __new_node = __construct_node(__src->__value_);
unique_ptr<__node, __tree_deleter> __left(
__copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
__node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
__node_pointer __new_node_ptr = __new_node.release();
__new_node_ptr->__is_black_ = __src->__is_black_;
__new_node_ptr->__left_ = static_cast<__node_base_pointer>(__left.release());
__new_node_ptr->__right_ = static_cast<__node_base_pointer>(__right);
if (__new_node_ptr->__left_)
__new_node_ptr->__left_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
if (__new_node_ptr->__right_)
__new_node_ptr->__right_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
return __new_node_ptr;
}
// This copy assignment will always produce a correct red-black-tree assuming the incoming tree is correct, since our
// own tree is a red-black-tree and the incoming tree is a red-black-tree. The invariants of a red-black-tree are
// temporarily not met until all of the incoming red-black tree is copied.
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
_LIBCPP_HIDE_FROM_ABI
#endif
__node_pointer
__copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
if (!__src) {
destroy(__dest);
return nullptr;
}
__assign_value(__dest->__value_, __src->__value_);
__dest->__is_black_ = __src->__is_black_;
// If we already have a left node in the destination tree, reuse it and copy-assign recursively
if (__dest->__left_) {
__dest->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
static_cast<__node_pointer>(__dest->__left_), static_cast<__node_pointer>(__src->__left_)));
// Otherwise, we must create new nodes; copy-construct from here on
} else if (__src->__left_) {
auto __new_left = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
__dest->__left_ = static_cast<__node_base_pointer>(__new_left);
__new_left->__parent_ = static_cast<__end_node_pointer>(__dest);
}
// Identical to the left case above, just for the right nodes
if (__dest->__right_) {
__dest->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
static_cast<__node_pointer>(__dest->__right_), static_cast<__node_pointer>(__src->__right_)));
} else if (__src->__right_) {
auto __new_right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
__dest->__right_ = static_cast<__node_base_pointer>(__new_right);
__new_right->__parent_ = static_cast<__end_node_pointer>(__dest);
}
return __dest;
}
};
template <class _Tp, class _Compare, class _Allocator>
@@ -1277,11 +1375,22 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_poin
template <class _Tp, class _Compare, class _Allocator>
__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
if (this != std::addressof(__t)) {
value_comp() = __t.value_comp();
__copy_assign_alloc(__t);
__assign_multi(__t.begin(), __t.end());
if (this == std::addressof(__t))
return *this;
value_comp() = __t.value_comp();
__copy_assign_alloc(__t);
if (__size_ != 0) {
*__root_ptr() = static_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
} else {
*__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
if (__root())
__root()->__parent_ = __end_node();
}
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
__size_ = __t.size();
return *this;
}
@@ -1327,11 +1436,17 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
template <class _Tp, class _Compare, class _Allocator>
__tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
: __begin_node_(),
: __begin_node_(__end_node()),
__node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
__size_(0),
__value_comp_(__t.value_comp()) {
__begin_node_ = __end_node();
if (__t.size() == 0)
return;
*__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
__root()->__parent_ = __end_node();
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
__size_ = __t.size();
}
template <class _Tp, class _Compare, class _Allocator>
@@ -1430,13 +1545,7 @@ __tree<_Tp, _Compare, _Allocator>::~__tree() {
template <class _Tp, class _Compare, class _Allocator>
void __tree<_Tp, _Compare, _Allocator>::destroy(__node_pointer __nd) _NOEXCEPT {
if (__nd != nullptr) {
destroy(static_cast<__node_pointer>(__nd->__left_));
destroy(static_cast<__node_pointer>(__nd->__right_));
__node_allocator& __na = __node_alloc();
__node_traits::destroy(__na, std::addressof(__nd->__value_));
__node_traits::deallocate(__na, __nd, 1);
}
(__tree_deleter(__node_alloc_))(__nd);
}
template <class _Tp, class _Compare, class _Allocator>

View File

@@ -970,7 +970,7 @@ public:
: map(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
# endif
_LIBCPP_HIDE_FROM_ABI map(const map& __m) : __tree_(__m.__tree_) { insert(__m.begin(), __m.end()); }
_LIBCPP_HIDE_FROM_ABI map(const map& __m) = default;
_LIBCPP_HIDE_FROM_ABI map& operator=(const map& __m) = default;
@@ -1637,11 +1637,7 @@ public:
: multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
# endif
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m)
: __tree_(__m.__tree_.value_comp(),
__alloc_traits::select_on_container_copy_construction(__m.__tree_.__alloc())) {
insert(__m.begin(), __m.end());
}
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m) = default;
_LIBCPP_HIDE_FROM_ABI multimap& operator=(const multimap& __m) = default;

View File

@@ -660,7 +660,7 @@ public:
: set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
# endif
_LIBCPP_HIDE_FROM_ABI set(const set& __s) : __tree_(__s.__tree_) { insert(__s.begin(), __s.end()); }
_LIBCPP_HIDE_FROM_ABI set(const set& __s) = default;
_LIBCPP_HIDE_FROM_ABI set& operator=(const set& __s) = default;
@@ -1119,11 +1119,7 @@ public:
: multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
# endif
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s)
: __tree_(__s.__tree_.value_comp(),
__alloc_traits::select_on_container_copy_construction(__s.__tree_.__alloc())) {
insert(__s.begin(), __s.end());
}
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
_LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;

View File

@@ -151,7 +151,7 @@ void associative_container_benchmarks(std::string container) {
/////////////////////////
// Assignment
/////////////////////////
bench("operator=(const&)", [=](auto& st) {
bench("operator=(const&) (into cleared Container)", [=](auto& st) {
const std::size_t size = st.range(0);
std::vector<Value> in = make_value_types(generate_unique_keys(size));
Container src(in.begin(), in.end());
@@ -172,6 +172,42 @@ void associative_container_benchmarks(std::string container) {
}
});
bench("operator=(const&) (into partially populated Container)", [=](auto& st) {
const std::size_t size = st.range(0);
std::vector<Value> in = make_value_types(generate_unique_keys(size));
Container src(in.begin(), in.end());
Container c[BatchSize];
while (st.KeepRunningBatch(BatchSize)) {
for (std::size_t i = 0; i != BatchSize; ++i) {
c[i] = src;
benchmark::DoNotOptimize(c[i]);
benchmark::ClobberMemory();
}
st.PauseTiming();
for (std::size_t i = 0; i != BatchSize; ++i) {
c[i].clear();
}
st.ResumeTiming();
}
});
bench("operator=(const&) (into populated Container)", [=](auto& st) {
const std::size_t size = st.range(0);
std::vector<Value> in = make_value_types(generate_unique_keys(size));
Container src(in.begin(), in.end());
Container c[BatchSize];
while (st.KeepRunningBatch(BatchSize)) {
for (std::size_t i = 0; i != BatchSize; ++i) {
c[i] = src;
benchmark::DoNotOptimize(c[i]);
benchmark::ClobberMemory();
}
}
});
/////////////////////////
// Insertion
/////////////////////////

View File

@@ -29,6 +29,7 @@ struct support::adapt_operations<std::map<K, V>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::map<int, int>>("std::map<int, int>");
support::associative_container_benchmarks<std::map<std::string, int>>("std::map<std::string, int>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();

View File

@@ -28,6 +28,7 @@ struct support::adapt_operations<std::multimap<K, V>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::multimap<int, int>>("std::multimap<int, int>");
support::associative_container_benchmarks<std::multimap<std::string, int>>("std::multimap<std::string, int>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();

View File

@@ -26,6 +26,7 @@ struct support::adapt_operations<std::multiset<K>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::multiset<int>>("std::multiset<int>");
support::associative_container_benchmarks<std::multiset<std::string>>("std::multiset<std::string>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();

View File

@@ -27,6 +27,7 @@ struct support::adapt_operations<std::set<K>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::set<int>>("std::set<int>");
support::associative_container_benchmarks<std::set<std::string>>("std::set<std::string>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();

View File

@@ -324,6 +324,11 @@ void deque_test() {
void map_test() {
std::map<int, int> i_am_empty{};
// Make __tree_itertor available in the debug info
// FIXME: Is there any way to avoid this requirement?
(void)i_am_empty.begin();
ComparePrettyPrintToChars(i_am_empty, "std::map is empty");
std::map<int, std::string> one_two_three;

View File

@@ -12,116 +12,129 @@
// map(const map& m);
#include <map>
#include <cassert>
#include <map>
#include "test_macros.h"
#include "min_allocator.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
#include "min_allocator.h"
template <template <class> class Alloc>
void test_alloc() {
{ // Simple check
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc<V> >;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr));
Map copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty map
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc<V> >;
const Map orig;
Map copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc<V> >;
V arr[] = {V(1, 1), V(2, 3), V(3, 6), V(4, 7), V(5, 0)};
const Map orig(begin(arr), end(arr));
Map copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(*std::next(copy.begin(), 3) == V(4, 7));
assert(*std::next(copy.begin(), 4) == V(5, 0));
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(*std::next(orig.begin(), 3) == V(4, 7));
assert(*std::next(orig.begin(), 4) == V(5, 0));
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc<std::allocator>();
test_alloc<min_allocator>(); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
using V = std::pair<const int, int>;
using Map = std::map<int, int, test_less<int> >;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr), test_less<int>(3));
Map copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
{ // Ensure that the allocator is copied
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, test_allocator<V> >;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr), std::less<int>(), test_allocator<V>(10));
Map copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == test_allocator<V>(10));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == test_allocator<V>(10));
assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
}
{ // Ensure that soccc is handled properly
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, other_allocator<V> >;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr), std::less<int>(), other_allocator<V>(10));
Map copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == other_allocator<V>(-2));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == other_allocator<V>(10));
}
}
int main(int, char**) {
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::map<int, double, C, A> m = mo;
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
#if TEST_STD_VER >= 11
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::map<int, double, C, A> m = mo;
assert(m.get_allocator() == A(-2));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef min_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
std::map<int, double, C, A> m = mo;
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
#endif
test();
return 0;
}

View File

@@ -12,116 +12,100 @@
// map(const map& m, const allocator_type& a);
#include <map>
#include <cassert>
#include <map>
#include "test_macros.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
#include "min_allocator.h"
template <class Alloc>
void test_alloc(const Alloc& new_alloc) {
{ // Simple check
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr));
Map copy(orig, new_alloc);
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(std::next(copy.begin(), 3) == copy.end());
assert(copy.get_allocator() == new_alloc);
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty map
using Map = std::map<int, int, std::less<int>, Alloc>;
const Map orig;
Map copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(3, 6), V(4, 7), V(5, 0)};
const Map orig(begin(arr), end(arr));
Map copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(*std::next(copy.begin(), 3) == V(4, 7));
assert(*std::next(copy.begin(), 4) == V(5, 0));
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(*std::next(orig.begin(), 3) == V(4, 7));
assert(*std::next(orig.begin(), 4) == V(5, 0));
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc(std::allocator<std::pair<const int, int> >());
test_alloc(test_allocator<std::pair<const int, int> >(25)); // Make sure that the new allocator is actually used
test_alloc(min_allocator<std::pair<const int, int> >()); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
using V = std::pair<const int, int>;
using Map = std::map<int, int, test_less<int> >;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr), test_less<int>(3));
Map copy(orig, std::allocator<V>());
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
}
int main(int, char**) {
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::map<int, double, C, A> m(mo, A(3));
assert(m.get_allocator() == A(3));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
#if TEST_STD_VER >= 11
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef min_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
std::map<int, double, C, A> m(mo, A());
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef explicit_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A{});
std::map<int, double, C, A> m(mo, A{});
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
#endif
test();
return 0;
}

View File

@@ -12,11 +12,11 @@
// map& operator=(const map& m);
#include <map>
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <iterator>
#include <map>
#include <vector>
#include "test_macros.h"
@@ -24,274 +24,286 @@
#include "test_allocator.h"
#include "min_allocator.h"
#if TEST_STD_VER >= 11
std::vector<int> ca_allocs;
std::vector<int> ca_deallocs;
template <class T>
class counting_allocatorT {
public:
typedef T value_type;
int foo{0};
counting_allocatorT(int f) noexcept : foo(f) {}
class tracking_allocator {
std::vector<void*>* allocs_;
template <class U>
friend class tracking_allocator;
public:
using value_type = T;
using propagate_on_container_copy_assignment = std::true_type;
tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
template <class U>
counting_allocatorT(const counting_allocatorT<U>& other) noexcept {
foo = other.foo;
}
template <class U>
bool operator==(const counting_allocatorT<U>& other) const noexcept {
return foo == other.foo;
}
template <class U>
bool operator!=(const counting_allocatorT<U>& other) const noexcept {
return foo != other.foo;
tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
T* allocate(std::size_t n) {
T* allocation = std::allocator<T>().allocate(n);
allocs_->push_back(allocation);
return allocation;
}
T* allocate(std::size_t n) const {
ca_allocs.push_back(foo);
void* const pv = ::malloc(n * sizeof(T));
return static_cast<T*>(pv);
void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
allocs_->erase(res);
std::allocator<T>().deallocate(ptr, n);
}
void deallocate(T* p, std::size_t) const noexcept {
ca_deallocs.push_back(foo);
free(p);
friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ == rhs.allocs_;
}
friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ != rhs.allocs_;
}
};
template <class T>
class counting_allocatorF {
public:
typedef T value_type;
int foo{0};
counting_allocatorF(int f) noexcept : foo(f) {}
using propagate_on_container_copy_assignment = std::false_type;
template <class U>
counting_allocatorF(const counting_allocatorF<U>& other) noexcept {
foo = other.foo;
}
template <class U>
bool operator==(const counting_allocatorF<U>& other) const noexcept {
return foo == other.foo;
}
template <class U>
bool operator!=(const counting_allocatorF<U>& other) const noexcept {
return foo != other.foo;
}
T* allocate(std::size_t n) const {
ca_allocs.push_back(foo);
void* const pv = ::malloc(n * sizeof(T));
return static_cast<T*>(pv);
}
void deallocate(T* p, std::size_t) const noexcept {
ca_deallocs.push_back(foo);
free(p);
}
struct NoOp {
void operator()() {}
};
bool balanced_allocs() {
std::vector<int> temp1, temp2;
template <class Alloc, class AllocatorInvariant = NoOp>
void test_alloc(const Alloc& lhs_alloc = Alloc(),
const Alloc& rhs_alloc = Alloc(),
AllocatorInvariant check_alloc_invariant = NoOp()) {
{ // Test empty/non-empy map combinations
{ // assign from a non-empty container into an empty one
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
std::printf("Allocations = %zu, deallocations = %zu\n", ca_allocs.size(), ca_deallocs.size());
if (ca_allocs.size() != ca_deallocs.size())
return false;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
Map copy(lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(std::next(copy.begin(), 3) == copy.end());
temp1 = ca_allocs;
std::sort(temp1.begin(), temp1.end());
temp2.clear();
std::unique_copy(temp1.begin(), temp1.end(), std::back_inserter<std::vector<int>>(temp2));
std::printf("There were %zu different allocators\n", temp2.size());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into an empty one
using Map = std::map<int, int, std::less<int>, Alloc>;
for (std::vector<int>::const_iterator it = temp2.begin(); it != temp2.end(); ++it) {
std::ptrdiff_t const allocs = std::count(ca_allocs.begin(), ca_allocs.end(), *it);
std::ptrdiff_t const deallocs = std::count(ca_deallocs.begin(), ca_deallocs.end(), *it);
std::printf("%d: %td vs %td\n", *it, allocs, deallocs);
if (allocs != deallocs)
return false;
const Map orig(rhs_alloc);
Map copy(lhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into a non-empty one
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(rhs_alloc);
Map copy(begin(arr), end(arr), std::less<int>(), rhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
temp1 = ca_allocs;
std::sort(temp1.begin(), temp1.end());
temp2.clear();
std::unique_copy(temp1.begin(), temp1.end(), std::back_inserter<std::vector<int>>(temp2));
std::printf("There were %zu different (de)allocators\n", temp2.size());
{ // Ensure that self-assignment works correctly
{ // with a non-empty map
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
for (std::vector<int>::const_iterator it = ca_deallocs.begin(); it != ca_deallocs.end(); ++it) {
std::ptrdiff_t const allocs = std::count(ca_allocs.begin(), ca_allocs.end(), *it);
std::ptrdiff_t const deallocs = std::count(ca_deallocs.begin(), ca_deallocs.end(), *it);
std::printf("%d: %td vs %td\n", *it, allocs, deallocs);
if (allocs != deallocs)
return false;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
orig = static_cast<const Map&>(orig);
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // with an empty map
using Map = std::map<int, int, std::less<int>, Alloc>;
Map orig(rhs_alloc);
orig = static_cast<const Map&>(orig);
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
return true;
{ // check assignment into a non-empty map
check_alloc_invariant();
{ // LHS already contains elements, but fewer than the RHS
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
V lhs_arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
V rhs_arr[] = {V(4, 2), V(5, 1)};
Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS contains the same number of elements as the RHS
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
V lhs_arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0)};
Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS already contains more elements than the RHS
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
V lhs_arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0), V(7, 9)};
Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(3, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(3, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
}
{ // Make a somewhat larget set to exercise the algorithm a bit
using V = std::pair<const int, int>;
using Map = std::map<int, int, std::less<int>, Alloc>;
Map orig(rhs_alloc);
for (int i = 0; i != 50; ++i)
orig[i] = i + 3;
Map copy(lhs_alloc);
copy = orig;
int i = 0;
for (auto v : copy) {
assert(v == V(i, i + 3));
++i;
}
}
check_alloc_invariant();
}
void test() {
test_alloc<std::allocator<std::pair<const int, int> > >();
#if TEST_STD_VER >= 11
test_alloc<min_allocator<std::pair<const int, int> > >();
{ // Make sure we're allocating/deallocating nodes with the correct allocator
// See https://llvm.org/PR29001
class AssertEmpty {
std::vector<void*>* lhs_allocs_;
std::vector<void*>* rhs_allocs_;
public:
AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
: lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
void operator()() {
assert(lhs_allocs_->empty());
assert(rhs_allocs_->empty());
}
};
std::vector<void*> lhs_allocs;
std::vector<void*> rhs_allocs;
test_alloc<tracking_allocator<std::pair<const int, int> > >(
lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
}
#endif
{ // Ensure that the comparator is copied
using V = std::pair<const int, int>;
using Map = std::map<int, int, test_less<int> >;
V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
const Map orig(begin(arr), end(arr), test_less<int>(3));
Map copy;
copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
{ // Make sure the color is copied as well
std::map<int, int> in;
for (int i = 0; i != 32; ++i)
in[i] = i;
std::map<int, int> out = in;
out.erase(std::next(out.begin(), 10), out.end());
out = in;
out.erase(std::next(out.begin(), 17), out.end());
}
}
int main(int, char**) {
{
typedef std::pair<const int, double> V;
V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
{
typedef std::pair<const int, double> V;
const V ar[] = {
V(1, 1),
V(2, 1),
V(3, 1),
};
std::map<int, double> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
std::map<int, double>* p = &m;
m = *p;
assert(m.size() == 3);
assert(std::equal(m.begin(), m.end(), ar));
}
{
typedef std::pair<const int, double> V;
V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m.get_allocator() == A(2));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
#if TEST_STD_VER >= 11
{
typedef std::pair<const int, double> V;
V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
typedef test_less<int> C;
typedef min_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A());
m = mo;
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
{
typedef std::pair<const int, double> V;
V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
typedef test_less<int> C;
typedef min_allocator<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A());
m = mo;
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
assert(balanced_allocs());
{
typedef std::pair<const int, double> V;
V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
typedef test_less<int> C;
typedef counting_allocatorT<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(1));
std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(2));
m = mo;
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
assert(balanced_allocs());
{
typedef std::pair<const int, double> V;
V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
typedef test_less<int> C;
typedef counting_allocatorF<V> A;
std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(100));
std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(200));
m = mo;
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == V(1, 1));
assert(*std::next(m.begin()) == V(2, 1));
assert(*std::next(m.begin(), 2) == V(3, 1));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == V(1, 1));
assert(*std::next(mo.begin()) == V(2, 1));
assert(*std::next(mo.begin(), 2) == V(3, 1));
}
assert(balanced_allocs());
#endif
test();
return 0;
}

View File

@@ -20,81 +20,122 @@
#include "test_allocator.h"
#include "min_allocator.h"
template <template <class> class Alloc>
void test_alloc() {
{ // Simple check
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc<V> >;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr));
Map copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty map
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc<V> >;
const Map orig;
Map copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc<V> >;
V arr[] = {V(1, 1), V(2, 3), V(2, 6), V(4, 7), V(5, 0)};
const Map orig(begin(arr), end(arr));
Map copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(*std::next(copy.begin(), 3) == V(4, 7));
assert(*std::next(copy.begin(), 4) == V(5, 0));
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(*std::next(orig.begin(), 3) == V(4, 7));
assert(*std::next(orig.begin(), 4) == V(5, 0));
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc<std::allocator>();
test_alloc<min_allocator>(); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, test_less<int> >;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr), test_less<int>(3));
Map copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
{ // Ensure that the allocator is copied
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, test_allocator<V> >;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr), std::less<int>(), test_allocator<V>(10));
Map copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == test_allocator<V>(10));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == test_allocator<V>(10));
assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
}
{ // Ensure that soccc is handled properly
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, other_allocator<V> >;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr), std::less<int>(), other_allocator<V>(10));
Map copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == other_allocator<V>(-2));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == other_allocator<V>(10));
}
}
int main(int, char**) {
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::multimap<int, double, C, A> m = mo;
assert(m == mo);
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
}
#if TEST_STD_VER >= 11
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::multimap<int, double, C, A> m = mo;
assert(m == mo);
assert(m.get_allocator() == A(-2));
assert(m.key_comp() == C(5));
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
}
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef min_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
std::multimap<int, double, C, A> m = mo;
assert(m == mo);
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
}
#endif
test();
return 0;
}

View File

@@ -20,81 +20,92 @@
#include "test_allocator.h"
#include "min_allocator.h"
template <class Alloc>
void test_alloc(const Alloc& new_alloc) {
{ // Simple check
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr));
Map copy(orig, new_alloc);
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(std::next(copy.begin(), 3) == copy.end());
assert(copy.get_allocator() == new_alloc);
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty map
using Map = std::multimap<int, int, std::less<int>, Alloc>;
const Map orig;
Map copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(2, 6), V(4, 7), V(5, 0)};
const Map orig(begin(arr), end(arr));
Map copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(*std::next(copy.begin(), 3) == V(4, 7));
assert(*std::next(copy.begin(), 4) == V(5, 0));
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(*std::next(orig.begin(), 3) == V(4, 7));
assert(*std::next(orig.begin(), 4) == V(5, 0));
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc(std::allocator<std::pair<const int, int> >());
test_alloc(test_allocator<std::pair<const int, int> >(25)); // Make sure that the new allocator is actually used
test_alloc(min_allocator<std::pair<const int, int> >()); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, test_less<int> >;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr), test_less<int>(3));
Map copy(orig, std::allocator<V>());
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
}
int main(int, char**) {
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::multimap<int, double, C, A> m(mo, A(3));
assert(m == mo);
assert(m.get_allocator() == A(3));
assert(m.key_comp() == C(5));
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
}
#if TEST_STD_VER >= 11
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef min_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
std::multimap<int, double, C, A> m(mo, A());
assert(m == mo);
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
}
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef explicit_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A{});
std::multimap<int, double, C, A> m(mo, A{});
assert(m == mo);
assert(m.get_allocator() == A{});
assert(m.key_comp() == C(5));
assert(mo.get_allocator() == A{});
assert(mo.key_comp() == C(5));
}
#endif
test();
return 0;
}

View File

@@ -12,111 +12,286 @@
// multimap& operator=(const multimap& m);
#include <map>
#include <algorithm>
#include <cassert>
#include <map>
#include <vector>
#include "test_macros.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
#include "min_allocator.h"
int main(int, char**) {
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m == mo);
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
template <class T>
class tracking_allocator {
std::vector<void*>* allocs_;
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
}
{
typedef std::pair<const int, double> V;
const V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
std::multimap<int, double> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
std::multimap<int, double>* p = &m;
m = *p;
assert(m.size() == sizeof(ar) / sizeof(ar[0]));
assert(std::equal(m.begin(), m.end(), ar));
}
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m == mo);
assert(m.get_allocator() == A(2));
assert(m.key_comp() == C(5));
template <class U>
friend class tracking_allocator;
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
public:
using value_type = T;
using propagate_on_container_copy_assignment = std::true_type;
tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
template <class U>
tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
T* allocate(std::size_t n) {
T* allocation = std::allocator<T>().allocate(n);
allocs_->push_back(allocation);
return allocation;
}
void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
allocs_->erase(res);
std::allocator<T>().deallocate(ptr, n);
}
friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ == rhs.allocs_;
}
friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ != rhs.allocs_;
}
};
struct NoOp {
void operator()() {}
};
template <class Alloc, class AllocatorInvariant = NoOp>
void test_alloc(const Alloc& lhs_alloc = Alloc(),
const Alloc& rhs_alloc = Alloc(),
AllocatorInvariant check_alloc_invariant = NoOp()) {
{ // Test empty/non-empy multimap combinations
{ // assign from a non-empty container into an empty one
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
Map copy(lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into an empty one
using Map = std::multimap<int, int, std::less<int>, Alloc>;
const Map orig(rhs_alloc);
Map copy(lhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into a non-empty one
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(rhs_alloc);
Map copy(begin(arr), end(arr), std::less<int>(), rhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
{ // Ensure that self-assignment works correctly
{ // with a non-empty multimap
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
orig = static_cast<const Map&>(orig);
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // with an empty multimap
using Map = std::multimap<int, int, std::less<int>, Alloc>;
Map orig(rhs_alloc);
orig = static_cast<const Map&>(orig);
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
{ // check assignment into a non-empty multimap
check_alloc_invariant();
{ // LHS already contains elements, but fewer than the RHS
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V lhs_arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
V rhs_arr[] = {V(4, 2), V(5, 1)};
Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS contains the same number of elements as the RHS
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V lhs_arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0)};
Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS already contains more elements than the RHS
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
V lhs_arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0), V(7, 9)};
Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == V(1, 1));
assert(*std::next(copy.begin(), 1) == V(2, 3));
assert(*std::next(copy.begin(), 2) == V(2, 6));
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == V(1, 1));
assert(*std::next(orig.begin(), 1) == V(2, 3));
assert(*std::next(orig.begin(), 2) == V(2, 6));
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
}
{ // Make a somewhat larget set to exercise the algorithm a bit
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, std::less<int>, Alloc>;
Map orig(rhs_alloc);
for (int i = 0; i != 50; ++i) {
orig.insert(V(i, i + 3));
orig.insert(V(i, i + 5));
}
Map copy(lhs_alloc);
copy = orig;
int i = 0;
for (auto iter = copy.begin(); iter != copy.end();) {
assert(*iter++ == V(i, i + 3));
assert(*iter++ == V(i, i + 5));
++i;
}
}
check_alloc_invariant();
}
void test() {
test_alloc<std::allocator<std::pair<const int, int> > >();
#if TEST_STD_VER >= 11
{
typedef std::pair<const int, double> V;
V ar[] = {
V(1, 1),
V(1, 1.5),
V(1, 2),
V(2, 1),
V(2, 1.5),
V(2, 2),
V(3, 1),
V(3, 1.5),
V(3, 2),
};
typedef test_less<int> C;
typedef min_allocator<V> A;
std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A());
m = mo;
assert(m == mo);
assert(m.get_allocator() == A());
assert(m.key_comp() == C(5));
test_alloc<min_allocator<std::pair<const int, int> > >();
assert(mo.get_allocator() == A());
assert(mo.key_comp() == C(5));
{ // Make sure we're allocating/deallocating nodes with the correct allocator
// See https://llvm.org/PR29001
class AssertEmpty {
std::vector<void*>* lhs_allocs_;
std::vector<void*>* rhs_allocs_;
public:
AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
: lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
void operator()() {
assert(lhs_allocs_->empty());
assert(rhs_allocs_->empty());
}
};
std::vector<void*> lhs_allocs;
std::vector<void*> rhs_allocs;
test_alloc<tracking_allocator<std::pair<const int, int> > >(
lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
}
#endif
{ // Ensure that the comparator is copied
using V = std::pair<const int, int>;
using Map = std::multimap<int, int, test_less<int> >;
V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
const Map orig(begin(arr), end(arr), test_less<int>(3));
Map copy;
copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
}
int main(int, char**) {
test();
return 0;
}

View File

@@ -16,83 +16,121 @@
#include <cassert>
#include <iterator>
#include "min_allocator.h"
#include "test_macros.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
template <template <class> class Alloc>
void test_alloc() {
{ // Simple check
using Set = std::multiset<int, std::less<int>, Alloc<int> >;
int arr[] = {1, 2, 2};
const Set orig(std::begin(arr), std::end(arr));
Set copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 2);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 2);
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty set
using Set = std::multiset<int, std::less<int>, Alloc<int> >;
const Set orig;
Set copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using Set = std::multiset<int, std::less<int>, Alloc<int> >;
int arr[] = {1, 2, 3, 3, 5};
const Set orig(std::begin(arr), std::end(arr));
Set copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(*std::next(copy.begin(), 3) == 3);
assert(*std::next(copy.begin(), 4) == 5);
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(*std::next(orig.begin(), 3) == 3);
assert(*std::next(orig.begin(), 4) == 5);
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc<std::allocator>();
test_alloc<min_allocator>(); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
using Set = std::multiset<int, test_less<int> >;
int arr[] = {1, 2, 2};
const Set orig(std::begin(arr), std::end(arr), test_less<int>(3));
Set copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
{ // Ensure that the allocator is copied
using Set = std::multiset<int, std::less<int>, test_allocator<int> >;
int arr[] = {1, 2, 2};
const Set orig(std::begin(arr), std::end(arr), std::less<int>(), test_allocator<int>(10));
Set copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == test_allocator<int>(10));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == test_allocator<int>(10));
assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
}
{ // Ensure that soccc is handled properly
using Set = std::multiset<int, std::less<int>, other_allocator<int> >;
int arr[] = {1, 2, 2};
const Set orig(std::begin(arr), std::end(arr), std::less<int>(), other_allocator<int>(10));
Set copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == other_allocator<int>(-2));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == other_allocator<int>(10));
}
}
int main(int, char**) {
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::multiset<int, C, A> m = mo;
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
assert(m.size() == 9);
assert(std::distance(m.begin(), m.end()) == 9);
assert(*std::next(m.begin(), 0) == 1);
assert(*std::next(m.begin(), 1) == 1);
assert(*std::next(m.begin(), 2) == 1);
assert(*std::next(m.begin(), 3) == 2);
assert(*std::next(m.begin(), 4) == 2);
assert(*std::next(m.begin(), 5) == 2);
assert(*std::next(m.begin(), 6) == 3);
assert(*std::next(m.begin(), 7) == 3);
assert(*std::next(m.begin(), 8) == 3);
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 9);
assert(std::distance(mo.begin(), mo.end()) == 9);
assert(*std::next(mo.begin(), 0) == 1);
assert(*std::next(mo.begin(), 1) == 1);
assert(*std::next(mo.begin(), 2) == 1);
assert(*std::next(mo.begin(), 3) == 2);
assert(*std::next(mo.begin(), 4) == 2);
assert(*std::next(mo.begin(), 5) == 2);
assert(*std::next(mo.begin(), 6) == 3);
assert(*std::next(mo.begin(), 7) == 3);
assert(*std::next(mo.begin(), 8) == 3);
}
#if TEST_STD_VER >= 11
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::multiset<int, C, A> m = mo;
assert(m.get_allocator() == A(-2));
assert(m.key_comp() == C(5));
assert(m.size() == 9);
assert(std::distance(m.begin(), m.end()) == 9);
assert(*std::next(m.begin(), 0) == 1);
assert(*std::next(m.begin(), 1) == 1);
assert(*std::next(m.begin(), 2) == 1);
assert(*std::next(m.begin(), 3) == 2);
assert(*std::next(m.begin(), 4) == 2);
assert(*std::next(m.begin(), 5) == 2);
assert(*std::next(m.begin(), 6) == 3);
assert(*std::next(m.begin(), 7) == 3);
assert(*std::next(m.begin(), 8) == 3);
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 9);
assert(std::distance(mo.begin(), mo.end()) == 9);
assert(*std::next(mo.begin(), 0) == 1);
assert(*std::next(mo.begin(), 1) == 1);
assert(*std::next(mo.begin(), 2) == 1);
assert(*std::next(mo.begin(), 3) == 2);
assert(*std::next(mo.begin(), 4) == 2);
assert(*std::next(mo.begin(), 5) == 2);
assert(*std::next(mo.begin(), 6) == 3);
assert(*std::next(mo.begin(), 7) == 3);
assert(*std::next(mo.begin(), 8) == 3);
}
#endif
test();
return 0;
}

View File

@@ -16,44 +16,92 @@
#include <cassert>
#include <iterator>
#include "min_allocator.h"
#include "test_macros.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
int main(int, char**) {
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::multiset<int, C, A> m(mo, A(3));
assert(m.get_allocator() == A(3));
assert(m.key_comp() == C(5));
assert(m.size() == 9);
assert(std::distance(m.begin(), m.end()) == 9);
assert(*std::next(m.begin(), 0) == 1);
assert(*std::next(m.begin(), 1) == 1);
assert(*std::next(m.begin(), 2) == 1);
assert(*std::next(m.begin(), 3) == 2);
assert(*std::next(m.begin(), 4) == 2);
assert(*std::next(m.begin(), 5) == 2);
assert(*std::next(m.begin(), 6) == 3);
assert(*std::next(m.begin(), 7) == 3);
assert(*std::next(m.begin(), 8) == 3);
template <class Alloc>
void test_alloc(const Alloc& new_alloc) {
{ // Simple check
using Set = std::multiset<int, std::less<int>, Alloc>;
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 9);
assert(std::distance(mo.begin(), mo.end()) == 9);
assert(*std::next(mo.begin(), 0) == 1);
assert(*std::next(mo.begin(), 1) == 1);
assert(*std::next(mo.begin(), 2) == 1);
assert(*std::next(mo.begin(), 3) == 2);
assert(*std::next(mo.begin(), 4) == 2);
assert(*std::next(mo.begin(), 5) == 2);
assert(*std::next(mo.begin(), 6) == 3);
assert(*std::next(mo.begin(), 7) == 3);
assert(*std::next(mo.begin(), 8) == 3);
int arr[] = {1, 2, 2};
const Set orig(std::begin(arr), std::end(arr));
Set copy(orig, new_alloc);
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 2);
assert(std::next(copy.begin(), 3) == copy.end());
assert(copy.get_allocator() == new_alloc);
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 2);
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty set
using Set = std::multiset<int, std::less<int>, Alloc>;
const Set orig;
Set copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using Set = std::multiset<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 3, 3, 5};
const Set orig(std::begin(arr), std::end(arr));
Set copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(*std::next(copy.begin(), 3) == 3);
assert(*std::next(copy.begin(), 4) == 5);
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(*std::next(orig.begin(), 3) == 3);
assert(*std::next(orig.begin(), 4) == 5);
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc(std::allocator<int>());
test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
test_alloc(min_allocator<int>()); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
int arr[] = {1, 2, 2};
const std::multiset<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
std::multiset<int, test_less<int> > copy(orig, std::allocator<int>());
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
}
int main(int, char**) {
test();
return 0;
}

View File

@@ -12,96 +12,271 @@
// multiset& operator=(const multiset& s);
#include <set>
#include <algorithm>
#include <cassert>
#include <iterator>
#include <set>
#include <vector>
#include "test_macros.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "test_macros.h"
template <class T>
class tracking_allocator {
std::vector<void*>* allocs_;
template <class U>
friend class tracking_allocator;
public:
using value_type = T;
using propagate_on_container_copy_assignment = std::true_type;
tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
template <class U>
tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
T* allocate(std::size_t n) {
T* allocation = std::allocator<T>().allocate(n);
allocs_->push_back(allocation);
return allocation;
}
void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
allocs_->erase(res);
std::allocator<T>().deallocate(ptr, n);
}
friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ == rhs.allocs_;
}
friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ != rhs.allocs_;
}
};
struct NoOp {
void operator()() {}
};
template <class Alloc, class AllocatorInvariant = NoOp>
void test_alloc(const Alloc& lhs_alloc = Alloc(),
const Alloc& rhs_alloc = Alloc(),
AllocatorInvariant check_alloc_invariant = NoOp()) {
{ // Test empty/non-empy multiset combinations
{ // assign from a non-empty container into an empty one
using Set = std::multiset<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 2};
const Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
Set copy(lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 2);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 2);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into an empty one
using Set = std::multiset<int, std::less<int>, Alloc>;
const Set orig(rhs_alloc);
Set copy(lhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into a non-empty one
using Set = std::multiset<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 2};
const Set orig(rhs_alloc);
Set copy(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
{ // Ensure that self-assignment works correctly
{ // with a non-empty multiset
using Set = std::multiset<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 2};
Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
orig = static_cast<const Set&>(orig);
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 2);
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // with an empty multiset
using Set = std::multiset<int, std::less<int>, Alloc>;
Set orig(rhs_alloc);
orig = static_cast<const Set&>(orig);
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
{ // check assignment into a non-empty multiset
check_alloc_invariant();
{ // LHS already contains elements, but fewer than the RHS
using Set = std::multiset<int, std::less<int>, Alloc>;
int lhs_arr[] = {1, 2, 2};
const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
int rhs_arr[] = {4, 5};
Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 2);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 2);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS contains the same number of elements as the RHS
using Set = std::multiset<int, std::less<int>, Alloc>;
int lhs_arr[] = {1, 2, 2};
const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
int rhs_arr[] = {4, 5, 6};
Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 2);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 2);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS already contains more elements than the RHS
using Set = std::multiset<int, std::less<int>, Alloc>;
int lhs_arr[] = {1, 2, 2};
const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
int rhs_arr[] = {4, 5, 6};
Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 2);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 2);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // Make a somewhat larget multiset to exercise the algorithm a bit
using Set = std::multiset<int, std::less<int>, Alloc>;
Set orig(rhs_alloc);
for (int i = 0; i != 50; ++i)
orig.insert(i);
Set copy(lhs_alloc);
copy = orig;
int i = 0;
for (auto v : copy) {
assert(v == i++);
}
}
check_alloc_invariant();
}
}
void test() {
test_alloc<std::allocator<int> >();
#if TEST_STD_VER >= 11
test_alloc<min_allocator<int> >();
{ // Make sure we're allocating/deallocating nodes with the correct allocator
// See https://llvm.org/PR29001
class AssertEmpty {
std::vector<void*>* lhs_allocs_;
std::vector<void*>* rhs_allocs_;
public:
AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
: lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
void operator()() {
assert(lhs_allocs_->empty());
assert(rhs_allocs_->empty());
}
};
std::vector<void*> lhs_allocs;
std::vector<void*> rhs_allocs;
test_alloc<tracking_allocator<int> >(lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
}
#endif
{ // Ensure that the comparator is copied
int arr[] = {1, 2, 2};
const std::multiset<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
std::multiset<int, test_less<int> > copy;
copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
}
int main(int, char**) {
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::multiset<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
assert(m.size() == 9);
assert(std::distance(m.begin(), m.end()) == 9);
assert(*std::next(m.begin(), 0) == 1);
assert(*std::next(m.begin(), 1) == 1);
assert(*std::next(m.begin(), 2) == 1);
assert(*std::next(m.begin(), 3) == 2);
assert(*std::next(m.begin(), 4) == 2);
assert(*std::next(m.begin(), 5) == 2);
assert(*std::next(m.begin(), 6) == 3);
assert(*std::next(m.begin(), 7) == 3);
assert(*std::next(m.begin(), 8) == 3);
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
assert(mo.size() == 9);
assert(std::distance(mo.begin(), mo.end()) == 9);
assert(*std::next(mo.begin(), 0) == 1);
assert(*std::next(mo.begin(), 1) == 1);
assert(*std::next(mo.begin(), 2) == 1);
assert(*std::next(mo.begin(), 3) == 2);
assert(*std::next(mo.begin(), 4) == 2);
assert(*std::next(mo.begin(), 5) == 2);
assert(*std::next(mo.begin(), 6) == 3);
assert(*std::next(mo.begin(), 7) == 3);
assert(*std::next(mo.begin(), 8) == 3);
}
{
typedef int V;
const V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
std::multiset<int> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
std::multiset<int>* p = &m;
m = *p;
assert(m.size() == 9);
assert(std::equal(m.begin(), m.end(), ar));
}
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::multiset<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m.get_allocator() == A(2));
assert(m.key_comp() == C(5));
assert(m.size() == 9);
assert(std::distance(m.begin(), m.end()) == 9);
assert(*std::next(m.begin(), 0) == 1);
assert(*std::next(m.begin(), 1) == 1);
assert(*std::next(m.begin(), 2) == 1);
assert(*std::next(m.begin(), 3) == 2);
assert(*std::next(m.begin(), 4) == 2);
assert(*std::next(m.begin(), 5) == 2);
assert(*std::next(m.begin(), 6) == 3);
assert(*std::next(m.begin(), 7) == 3);
assert(*std::next(m.begin(), 8) == 3);
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
assert(mo.size() == 9);
assert(std::distance(mo.begin(), mo.end()) == 9);
assert(*std::next(mo.begin(), 0) == 1);
assert(*std::next(mo.begin(), 1) == 1);
assert(*std::next(mo.begin(), 2) == 1);
assert(*std::next(mo.begin(), 3) == 2);
assert(*std::next(mo.begin(), 4) == 2);
assert(*std::next(mo.begin(), 5) == 2);
assert(*std::next(mo.begin(), 6) == 3);
assert(*std::next(mo.begin(), 7) == 3);
assert(*std::next(mo.begin(), 8) == 3);
}
test();
return 0;
}

View File

@@ -12,63 +12,123 @@
// set(const set& m);
#include <set>
#include <cassert>
#include <iterator>
#include <set>
#include "test_macros.h"
#include "min_allocator.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
template <template <class> class Alloc>
void test_alloc() {
{ // Simple check
using Set = std::set<int, std::less<int>, Alloc<int> >;
int arr[] = {1, 2, 3};
const Set orig(std::begin(arr), std::end(arr));
Set copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty set
using Set = std::set<int, std::less<int>, Alloc<int> >;
const Set orig;
Set copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using Set = std::set<int, std::less<int>, Alloc<int> >;
int arr[] = {1, 2, 3, 4, 5};
const Set orig(std::begin(arr), std::end(arr));
Set copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(*std::next(copy.begin(), 3) == 4);
assert(*std::next(copy.begin(), 4) == 5);
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(*std::next(orig.begin(), 3) == 4);
assert(*std::next(orig.begin(), 4) == 5);
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc<std::allocator>();
test_alloc<min_allocator>(); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
using Set = std::set<int, test_less<int> >;
int arr[] = {1, 2, 3};
const Set orig(std::begin(arr), std::end(arr), test_less<int>(3));
Set copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
{ // Ensure that the allocator is copied
using Set = std::set<int, std::less<int>, test_allocator<int> >;
int arr[] = {1, 2, 3};
const Set orig(std::begin(arr), std::end(arr), std::less<int>(), test_allocator<int>(10));
Set copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == test_allocator<int>(10));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == test_allocator<int>(10));
assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
}
{ // Ensure that soccc is handled properly
using Set = std::set<int, std::less<int>, other_allocator<int> >;
int arr[] = {1, 2, 3};
const Set orig(std::begin(arr), std::end(arr), std::less<int>(), other_allocator<int>(10));
Set copy = orig;
assert(copy.size() == 3);
assert(copy.get_allocator() == other_allocator<int>(-2));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.get_allocator() == other_allocator<int>(10));
}
}
int main(int, char**) {
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::set<int, C, A> m = mo;
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == 1);
assert(*std::next(m.begin()) == 2);
assert(*std::next(m.begin(), 2) == 3);
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == 1);
assert(*std::next(mo.begin()) == 2);
assert(*std::next(mo.begin(), 2) == 3);
}
#if TEST_STD_VER >= 11
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::set<int, C, A> m = mo;
assert(m.get_allocator() == A(-2));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == 1);
assert(*std::next(m.begin()) == 2);
assert(*std::next(m.begin(), 2) == 3);
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == 1);
assert(*std::next(mo.begin()) == 2);
assert(*std::next(mo.begin(), 2) == 3);
}
#endif
test();
return 0;
}

View File

@@ -12,36 +12,95 @@
// set(const set& m, const allocator_type& a);
#include <set>
#include <cassert>
#include <iterator>
#include <set>
#include "test_macros.h"
#include "../../../test_compare.h"
#include "min_allocator.h"
#include "test_macros.h"
#include "test_allocator.h"
int main(int, char**) {
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
std::set<int, C, A> m(mo, A(3));
assert(m.get_allocator() == A(3));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == 1);
assert(*std::next(m.begin()) == 2);
assert(*std::next(m.begin(), 2) == 3);
template <class Alloc>
void test_alloc(const Alloc& new_alloc) {
{ // Simple check
using Set = std::set<int, std::less<int>, Alloc>;
assert(mo.get_allocator() == A(7));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == 1);
assert(*std::next(mo.begin()) == 2);
assert(*std::next(mo.begin(), 2) == 3);
int arr[] = {1, 2, 3};
const Set orig(std::begin(arr), std::end(arr));
Set copy(orig, new_alloc);
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(std::next(copy.begin(), 3) == copy.end());
assert(copy.get_allocator() == new_alloc);
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // copy empty set
using Set = std::set<int, std::less<int>, Alloc>;
const Set orig;
Set copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
{ // only some leaf nodes exist
using Set = std::set<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 3, 4, 5};
const Set orig(std::begin(arr), std::end(arr));
Set copy = orig;
assert(copy.size() == 5);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(*std::next(copy.begin(), 3) == 4);
assert(*std::next(copy.begin(), 4) == 5);
assert(std::next(copy.begin(), 5) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 5);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(*std::next(orig.begin(), 3) == 4);
assert(*std::next(orig.begin(), 4) == 5);
assert(std::next(orig.begin(), 5) == orig.end());
}
}
void test() {
test_alloc(std::allocator<int>());
test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
test_alloc(min_allocator<int>()); // Make sure that fancy pointers work
{ // Ensure that the comparator is copied
int arr[] = {1, 2, 3};
const std::set<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
std::set<int, test_less<int> > copy(orig, std::allocator<int>());
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
}
int main(int, char**) {
test();
return 0;
}

View File

@@ -12,83 +12,273 @@
// set& operator=(const set& s);
#include <set>
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <iterator>
#include <set>
#include <vector>
#include "test_macros.h"
#include "../../../test_compare.h"
#include "test_allocator.h"
#include "min_allocator.h"
template <class T>
class tracking_allocator {
std::vector<void*>* allocs_;
template <class U>
friend class tracking_allocator;
public:
using value_type = T;
using propagate_on_container_copy_assignment = std::true_type;
tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
template <class U>
tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
T* allocate(std::size_t n) {
T* allocation = std::allocator<T>().allocate(n);
allocs_->push_back(allocation);
return allocation;
}
void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
allocs_->erase(res);
std::allocator<T>().deallocate(ptr, n);
}
friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ == rhs.allocs_;
}
friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
return lhs.allocs_ != rhs.allocs_;
}
};
struct NoOp {
void operator()() {}
};
template <class Alloc, class AllocatorInvariant = NoOp>
void test_alloc(const Alloc& lhs_alloc = Alloc(),
const Alloc& rhs_alloc = Alloc(),
AllocatorInvariant check_alloc_invariant = NoOp()) {
{ // Test empty/non-empy set combinations
{ // assign from a non-empty container into an empty one
using Set = std::set<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 3};
const Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
Set copy(lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into an empty one
using Set = std::set<int, std::less<int>, Alloc>;
const Set orig(rhs_alloc);
Set copy(lhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
check_alloc_invariant();
{ // assign from an empty container into a non-empty one
using Set = std::set<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 3};
const Set orig(rhs_alloc);
Set copy(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
copy = orig;
assert(copy.size() == 0);
assert(copy.begin() == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
{ // Ensure that self-assignment works correctly
{ // with a non-empty set
using Set = std::set<int, std::less<int>, Alloc>;
int arr[] = {1, 2, 3};
Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
orig = static_cast<const Set&>(orig);
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(std::next(orig.begin(), 3) == orig.end());
}
{ // with an empty set
using Set = std::set<int, std::less<int>, Alloc>;
Set orig(rhs_alloc);
orig = static_cast<const Set&>(orig);
assert(orig.size() == 0);
assert(orig.begin() == orig.end());
}
}
{ // check assignment into a non-empty set
check_alloc_invariant();
{ // LHS already contains elements, but fewer than the RHS
using Set = std::set<int, std::less<int>, Alloc>;
int lhs_arr[] = {1, 2, 3};
const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
int rhs_arr[] = {4, 5};
Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS contains the same number of elements as the RHS
using Set = std::set<int, std::less<int>, Alloc>;
int lhs_arr[] = {1, 2, 3};
const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
int rhs_arr[] = {4, 5, 6};
Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // LHS already contains more elements than the RHS
using Set = std::set<int, std::less<int>, Alloc>;
int lhs_arr[] = {1, 2, 3};
const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
int rhs_arr[] = {4, 5, 6};
Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
copy = orig;
assert(copy.size() == 3);
assert(*std::next(copy.begin(), 0) == 1);
assert(*std::next(copy.begin(), 1) == 2);
assert(*std::next(copy.begin(), 2) == 3);
assert(std::next(copy.begin(), 3) == copy.end());
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(*std::next(orig.begin(), 0) == 1);
assert(*std::next(orig.begin(), 1) == 2);
assert(*std::next(orig.begin(), 2) == 3);
assert(std::next(orig.begin(), 3) == orig.end());
}
check_alloc_invariant();
{ // Make a somewhat larget set to exercise the algorithm a bit
using Set = std::set<int, std::less<int>, Alloc>;
Set orig(rhs_alloc);
for (int i = 0; i != 50; ++i)
orig.insert(i);
Set copy(lhs_alloc);
copy = orig;
int i = 0;
for (auto v : copy) {
assert(v == i++);
}
}
check_alloc_invariant();
}
}
void test() {
test_alloc<std::allocator<int> >();
#if TEST_STD_VER >= 11
test_alloc<min_allocator<int> >();
{ // Make sure we're allocating/deallocating nodes with the correct allocator
// See https://llvm.org/PR29001
class AssertEmpty {
std::vector<void*>* lhs_allocs_;
std::vector<void*>* rhs_allocs_;
public:
AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
: lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
void operator()() {
assert(lhs_allocs_->empty());
assert(rhs_allocs_->empty());
}
};
std::vector<void*> lhs_allocs;
std::vector<void*> rhs_allocs;
test_alloc<tracking_allocator<int> >(lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
}
#endif
{ // Ensure that the comparator is copied
int arr[] = {1, 2, 3};
const std::set<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
std::set<int, test_less<int> > copy;
copy = orig;
assert(copy.size() == 3);
assert(copy.key_comp() == test_less<int>(3));
// Check that orig is still what is expected
assert(orig.size() == 3);
assert(orig.key_comp() == test_less<int>(3));
}
}
int main(int, char**) {
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef test_allocator<V> A;
std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::set<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m.get_allocator() == A(7));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == 1);
assert(*std::next(m.begin()) == 2);
assert(*std::next(m.begin(), 2) == 3);
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == 1);
assert(*std::next(mo.begin()) == 2);
assert(*std::next(mo.begin(), 2) == 3);
}
{
typedef int V;
const V ar[] = {1, 2, 3};
std::set<int> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
std::set<int>* p = &m;
m = *p;
assert(m.size() == 3);
assert(std::equal(m.begin(), m.end(), ar));
}
{
typedef int V;
V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
typedef test_less<int> C;
typedef other_allocator<V> A;
std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
std::set<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
m = mo;
assert(m.get_allocator() == A(2));
assert(m.key_comp() == C(5));
assert(m.size() == 3);
assert(std::distance(m.begin(), m.end()) == 3);
assert(*m.begin() == 1);
assert(*std::next(m.begin()) == 2);
assert(*std::next(m.begin(), 2) == 3);
assert(mo.get_allocator() == A(2));
assert(mo.key_comp() == C(5));
assert(mo.size() == 3);
assert(std::distance(mo.begin(), mo.end()) == 3);
assert(*mo.begin() == 1);
assert(*std::next(mo.begin()) == 2);
assert(*std::next(mo.begin(), 2) == 3);
}
{ // Test with std::pair, since we have some special handling for pairs inside __tree
std::pair<int, int> arr[] = {
std::make_pair(1, 2), std::make_pair(2, 3), std::make_pair(3, 4), std::make_pair(4, 5)};
std::set<std::pair<int, int> > a(arr, arr + 4);
std::set<std::pair<int, int> > b;
b = a;
assert(a == b);
}
test();
return 0;
}