diff --git a/include/fast_io_core_impl/allocation/adapters.h b/include/fast_io_core_impl/allocation/adapters.h index 38dad7e6..5e46502e 100644 --- a/include/fast_io_core_impl/allocation/adapters.h +++ b/include/fast_io_core_impl/allocation/adapters.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once // To make a constexpr allocator, we need ::std::allocator. Because only new expression and // ::std::allocator::allocate are allowed in constexpr functions. See https://github.com/microsoft/STL/issues/1532 @@ -145,10 +145,16 @@ class generic_allocator_adapter if (false) #endif { - auto p{::operator new(n)}; + auto p{ +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + }; if (zero) { - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + ::fast_io::freestanding::bytes_clear_n(static_cast<::std::byte *>(p), n); } return p; } @@ -682,7 +688,11 @@ class generic_allocator_adapter #if __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_delete) + __builtin_operator_delete(p); +#else ::operator delete(p); +#endif } else #endif @@ -704,7 +714,11 @@ class generic_allocator_adapter #if __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_delete) + __builtin_operator_delete(p); +#else ::operator delete(p); +#endif } else #endif @@ -747,10 +761,16 @@ class generic_allocator_adapter if (false) #endif { - auto p{::operator new(n)}; + auto p{ +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + }; if (zero) { - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + ::fast_io::freestanding::bytes_clear_n(static_cast<::std::byte *>(p), n); } return p; } @@ -847,7 +867,13 @@ class generic_allocator_adapter if (false) #endif { - return ::operator new(n); + return +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + ; } if constexpr (::fast_io::details::has_allocate_aligned_impl) { @@ -874,7 +900,13 @@ class generic_allocator_adapter if (false) #endif { - return ::operator new(n); + return +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + ; } if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) { @@ -904,7 +936,13 @@ class generic_allocator_adapter if (false) #endif { - return {::operator new(n), n}; + return { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + , n}; } else { @@ -931,7 +969,13 @@ class generic_allocator_adapter if (false) #endif { - return {::operator new(n), n}; + return { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + , n}; } else { @@ -2062,7 +2106,7 @@ class generic_allocator_adapter if (false) #endif { - return ::operator new(n); + return static_cast(::fast_io::freestanding::allocator<::std::byte>{}.allocate(n)); } else { diff --git a/include/fast_io_core_impl/freestanding/algorithm.h b/include/fast_io_core_impl/freestanding/algorithm.h index 1d658024..b52ee38a 100644 --- a/include/fast_io_core_impl/freestanding/algorithm.h +++ b/include/fast_io_core_impl/freestanding/algorithm.h @@ -301,9 +301,21 @@ inline constexpr output_iter overlapped_copy_trivial(input_iter first, ::std::si { tempbufferptr[i] = first[i]; } - for (::std::size_t i{}; i != n; ++i) +#if __cpp_if_consteval >= 202106L + if consteval + { + for (::std::size_t i{}; i != n; ++i) + { + ::std::construct_at(::std::addressof(result[i]), ::std::move(tempbufferptr[i])); + } + } + else +#endif { - result[i] = ::std::move(tempbufferptr[i]); + for (::std::size_t i{}; i != n; ++i) + { + result[i] = ::std::move(tempbufferptr[i]); + } } return result + n; } diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index f9b871ee..b7efcd1f 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -432,12 +432,36 @@ inline constexpr auto operator<=>(::fast_io::containers::details::deque_iterator template inline constexpr void deque_destroy_trivial_common_align(controllerblocktype &controller, ::std::size_t aligns, ::std::size_t totalsz) noexcept { - for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) +#if __cpp_if_consteval >= 202106L + if consteval + { + if (controller.controller_start_ptr == nullptr) + { + return; + } + for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) + { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_delete) + __builtin_operator_delete(*i); +#else + ::operator delete(*i); +#endif + } + using controller_replacetype = typename controllerblocktype::replacetype; + ::std::size_t const element_count{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1)}; + ::fast_io::typed_generic_allocator_adapter::deallocate_n(controller.controller_start_ptr, element_count); + return; + } + else +#endif { - allocator::deallocate_aligned_n(*i, aligns, totalsz); + for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) + { + allocator::deallocate_aligned_n(*i, aligns, totalsz); + } + ::std::size_t const n{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1) * sizeof(void *)}; + allocator::deallocate_n(controller.controller_start_ptr, n); } - ::std::size_t const n{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1) * sizeof(void *)}; - allocator::deallocate_n(controller.controller_start_ptr, n); } template @@ -573,7 +597,8 @@ inline constexpr void deque_allocate_on_empty_common_with_n_impl(dequecontroltyp --allocated_blocks_count; auto &controller_block{controller.controller_block}; - using begin_ptrtype = typename dequecontroltype::replacetype *; + using replacetype = typename dequecontroltype::replacetype; + using begin_ptrtype = replacetype *; controller_block.controller_after_ptr = (controller_block.controller_start_ptr = allocated_blocks_ptr) + allocated_blocks_count; @@ -585,11 +610,29 @@ inline constexpr void deque_allocate_on_empty_common_with_n_impl(dequecontroltyp auto start_block_ptr{allocated_mid_block - initial_allocated_block_counts_half}; auto end_block_ptr{start_block_ptr + initial_allocated_block_counts}; - for (auto i{start_block_ptr}; i != end_block_ptr; ++i) +#if __cpp_if_consteval >= 202106L + if consteval + { + for (auto i{start_block_ptr}; i != end_block_ptr; ++i) + { + ::std::construct_at(i, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(bytes * sizeof(replacetype)) +#else + ::operator new(bytes * sizeof(replacetype)) +#endif + )); + } + } + else +#endif { - ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, bytes))); + for (auto i{start_block_ptr}; i != end_block_ptr; ++i) + { + ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, bytes * sizeof(replacetype)))); + } } - *end_block_ptr = nullptr; + ::std::construct_at(end_block_ptr, static_cast(nullptr)); controller_block.controller_start_reserved_ptr = start_block_ptr; controller_block.controller_after_reserved_ptr = end_block_ptr; } @@ -604,8 +647,12 @@ inline constexpr void deque_allocate_empty_single_block_impl(dequecontroltype &c auto begin_ptr{*controllerptr}; auto mid_ptr{begin_ptr + (bytes >> 1u)}; - controller.front_block = - controller.back_block = {begin_ptr, mid_ptr, controllerptr}; + controller.front_block.begin_ptr = begin_ptr; + controller.front_block.curr_ptr = mid_ptr; + controller.front_block.controller_ptr = controllerptr; + controller.back_block.begin_ptr = begin_ptr; + controller.back_block.curr_ptr = mid_ptr; + controller.back_block.controller_ptr = controllerptr; controller.front_end_ptr = controller.back_end_ptr = (begin_ptr + bytes); } @@ -661,22 +708,45 @@ inline constexpr void deque_allocate_init_blocks_dezeroing_impl(dequecontroltype ::std::size_t const half_blocks_count_least{blocks_count_least >> 1u}; ::std::size_t const offset{half_blocks_count - half_blocks_count_least}; auto reserve_start{start_ptr + offset}, reserve_after{reserve_start + blocks_count_least}; - using begin_ptrtype = typename dequecontroltype::replacetype *; - for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) + using replacetype = typename dequecontroltype::replacetype; + using begin_ptrtype = replacetype *; +#if __cpp_if_consteval >= 202106L + if consteval + { + for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) + { + ::std::construct_at(it, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(blockbytes * sizeof(replacetype)) +#else + ::operator new(blockbytes * sizeof(replacetype)) +#endif + )); + } + } + else +#endif { - ::std::construct_at(it, static_cast(allocator::allocate_aligned_conditional_zero(align, blockbytes, zeroing))); + for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) + { + ::std::construct_at(it, static_cast(allocator::allocate_aligned_conditional_zero(align, blockbytes * sizeof(replacetype), zeroing))); + } } ::std::construct_at(reserve_after, nullptr); - using replacetype = typename dequecontroltype::replacetype; - using begin_ptrtype = replacetype *; begin_ptrtype reserve_start_block{static_cast(*reserve_start)}; - controller.front_block = {reserve_start_block, reserve_start_block, reserve_start}; + controller.front_block.begin_ptr = reserve_start_block; + controller.front_block.curr_ptr = reserve_start_block; + controller.front_block.controller_ptr = reserve_start; controller.front_end_ptr = reserve_start_block + blockbytes; begin_ptrtype reserve_back_block{static_cast(reserve_after[-1])}; - controller.back_block = {reserve_back_block, reserve_back_block, reserve_after - 1}; + controller.back_block.begin_ptr = reserve_back_block; + controller.back_block.curr_ptr = reserve_back_block; + controller.back_block.controller_ptr = reserve_after - 1; controller.back_end_ptr = reserve_back_block + blockbytes; - controller.controller_block = { - start_ptr, reserve_start, reserve_after, start_ptr + blocks_count}; + controller.controller_block.controller_start_ptr = start_ptr; + controller.controller_block.controller_start_reserved_ptr = reserve_start; + controller.controller_block.controller_after_reserved_ptr = reserve_after; + controller.controller_block.controller_after_ptr = start_ptr + blocks_count; } template @@ -1471,10 +1541,28 @@ inline constexpr void deque_reserve_back_blocks_impl_none_empty(dequecontroltype controller.controller_block.controller_after_ptr-controller.controller_block.controller_start_ptr,"\n" "to_allocate_blocks=",to_allocate_blocks); #endif +#if __cpp_if_consteval >= 202106L + if consteval + { + for (auto e{pos + to_allocate_blocks}; pos != e; ++pos) + { + ::std::construct_at(pos, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(blockbytes * sizeof(replacetype)) +#else + ::operator new(blockbytes * sizeof(replacetype)) +#endif + )); + } + } + else +#endif + { for (auto e{pos + to_allocate_blocks}; pos != e; ++pos) { - ::std::construct_at(pos, static_cast(allocator::allocate_aligned(align, blockbytes))); + ::std::construct_at(pos, static_cast(allocator::allocate_aligned(align, blockbytes * sizeof(replacetype)))); } + } ::std::construct_at(pos, nullptr); controller.controller_block.controller_after_reserved_ptr = pos; } @@ -1653,10 +1741,28 @@ inline constexpr void deque_reserve_front_blocks_none_empty_impl(dequecontroltyp auto ed{new_controller_start_reserved_ptr}; new_controller_start_reserved_ptr -= to_allocate_blocks; +#if __cpp_if_consteval >= 202106L + if consteval + { + for (auto i{new_controller_start_reserved_ptr}; i != ed; ++i) + { + ::std::construct_at(i, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(blockbytes * sizeof(replacetype)) +#else + ::operator new(blockbytes * sizeof(replacetype)) +#endif + )); + } + } + else +#endif + { for (auto i{new_controller_start_reserved_ptr}; i != ed; ++i) { - ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, blockbytes))); + ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, blockbytes * sizeof(replacetype)))); } + } controller.controller_block.controller_start_reserved_ptr = new_controller_start_reserved_ptr; } } @@ -2576,7 +2682,23 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE grow_back(); } auto currptr{controller.back_block.curr_ptr}; - ::std::construct_at(currptr, ::std::forward(args)...); +#if __cpp_if_consteval >= 202106L + if consteval + { + if constexpr (::std::is_trivially_constructible_v) + { + *currptr = value_type{::std::forward(args)...}; + } + else + { + ::std::construct_at(currptr, ::std::forward(args)...); + } + } + else +#endif + { + ::std::construct_at(currptr, ::std::forward(args)...); + } controller.back_block.curr_ptr = currptr + 1; return *currptr; } @@ -2677,16 +2799,44 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE grow_front(); } auto front_curr_ptr{controller.front_block.curr_ptr}; - if constexpr (::std::is_nothrow_constructible_v) +#if __cpp_if_consteval >= 202106L + if consteval { - return *(controller.front_block.curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...)); + if constexpr (::std::is_trivially_constructible_v) + { + *(front_curr_ptr - 1) = value_type{::std::forward(args)...}; + controller.front_block.curr_ptr = front_curr_ptr - 1; + return *(front_curr_ptr - 1); + } + else + { + if constexpr (::std::is_nothrow_constructible_v) + { + return *(controller.front_block.curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...)); + } + else + { + emplace_front_guard guard(__builtin_addressof(this->controller)); + front_curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...); + guard.thisdeq = nullptr; + return *(controller.front_block.curr_ptr = front_curr_ptr); + } + } } else +#endif { - emplace_front_guard guard(__builtin_addressof(this->controller)); - front_curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...); - guard.thisdeq = nullptr; - return *(controller.front_block.curr_ptr = front_curr_ptr); + if constexpr (::std::is_nothrow_constructible_v) + { + return *(controller.front_block.curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...)); + } + else + { + emplace_front_guard guard(__builtin_addressof(this->controller)); + front_curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...); + guard.thisdeq = nullptr; + return *(controller.front_block.curr_ptr = front_curr_ptr); + } } } diff --git a/include/fast_io_dsal/impl/string.h b/include/fast_io_dsal/impl/string.h index b114f82e..26f4dd66 100644 --- a/include/fast_io_dsal/impl/string.h +++ b/include/fast_io_dsal/impl/string.h @@ -85,7 +85,9 @@ inline constexpr void string_heap_dilate_uncheck(::fast_io::containers::details: rsize = newcap - 1u; } } - imp = {ptr, ptr + strsize, ptr + rsize}; + imp.begin_ptr = ptr; + imp.curr_ptr = ptr + strsize; + imp.end_ptr = ptr + rsize; } template @@ -138,13 +140,17 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE using typed_allocator_type = typed_generic_allocator_adapter; auto [ptr, cap]{typed_allocator_type::allocate_at_least(2)}; ::std::construct_at(ptr, char_type{}); - this->imp = {ptr, ptr, ptr + static_cast(cap - 1u)}; + this->imp.begin_ptr = ptr; + this->imp.curr_ptr = ptr; + this->imp.end_ptr = ptr + static_cast(cap - 1u); } else #endif { char_type *const ncstr{const_cast(null_terminated_c_str_v)}; - this->imp = {ncstr, ncstr, ncstr}; + this->imp.begin_ptr = ncstr; + this->imp.curr_ptr = ncstr; + this->imp.end_ptr = ncstr; } } diff --git a/tests/0026.container/0003.deque/constexpr.cc b/tests/0026.container/0003.deque/constexpr.cc new file mode 100644 index 00000000..a9da08b9 --- /dev/null +++ b/tests/0026.container/0003.deque/constexpr.cc @@ -0,0 +1,284 @@ +#include +#include +#include + +#if (defined(__GNUC__) && __GNUC__ >= 15) || (defined(__clang_major__) && __clang_major__ >= 22) + +namespace +{ + +// --- Test 1: basic push_back and iteration --- +constexpr bool test_push_back() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + if (dq.size() != 3) + return false; + if (dq[0] != 1 || dq[1] != 2 || dq[2] != 3) + return false; + return true; +} +static_assert(test_push_back()); + +// --- Test 2: push_front and random access --- +constexpr bool test_push_front() +{ + ::fast_io::deque dq; + dq.push_front(10); + dq.push_front(20); + dq.push_front(30); + if (dq.size() != 3) + return false; + if (dq[0] != 30 || dq[1] != 20 || dq[2] != 10) + return false; + return true; +} +static_assert(test_push_front()); + +// --- Test 3: pop_back --- +constexpr bool test_pop_back() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + dq.pop_back(); + if (dq.size() != 2) + return false; + if (dq[0] != 1 || dq[1] != 2) + return false; + dq.pop_back(); + if (dq.size() != 1) + return false; + if (dq[0] != 1) + return false; + dq.pop_back(); + if (!dq.is_empty()) + return false; + return true; +} +static_assert(test_pop_back()); + +// --- Test 4: pop_front --- +constexpr bool test_pop_front() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + dq.pop_front(); + if (dq.size() != 2) + return false; + if (dq[0] != 2 || dq[1] != 3) + return false; + dq.pop_front(); + if (dq.size() != 1 || dq[0] != 3) + return false; + dq.pop_front(); + if (!dq.is_empty()) + return false; + return true; +} +static_assert(test_pop_front()); + +// --- Test 5: front() and back() --- +constexpr bool test_front_back() +{ + ::fast_io::deque dq; + dq.push_back(10); + dq.push_back(20); + dq.push_back(30); + if (dq.front() != 10 || dq.back() != 30) + return false; + dq.front() = 100; + dq.back() = 300; + if (dq[0] != 100 || dq[2] != 300) + return false; + return true; +} +static_assert(test_front_back()); + +// --- Test 6: clear() and reuse --- +constexpr bool test_clear() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + dq.clear(); + if (!dq.is_empty() || dq.size() != 0) + return false; + dq.push_back(42); + if (dq.size() != 1 || dq[0] != 42) + return false; + return true; +} +static_assert(test_clear()); + +// --- Test 7: copy construction --- +constexpr bool test_copy_construct() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2(dq); + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + if (dq.size() != 3) + return false; + if (dq[0] != 1 || dq[1] != 2 || dq[2] != 3) + return false; + return true; +} +static_assert(test_copy_construct()); + +// --- Test 8: move construction --- +constexpr bool test_move_construct() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2(::std::move(dq)); + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + return true; +} +static_assert(test_move_construct()); + +// --- Test 9: push_back sufficient elements to trigger growth --- +constexpr bool test_push_back_many() +{ + ::fast_io::deque dq; + // push enough to force multiple block allocations + for (int i{}; i != 5000; ++i) + { + dq.push_back(i); + } + if (dq.size() != 5000) + return false; + for (int i{}; i != 5000; ++i) + { + if (dq[i] != i) + return false; + } + return true; +} +static_assert(test_push_back_many()); + +// --- Test 10: mixed push_front and push_back --- +constexpr bool test_mixed_push() +{ + ::fast_io::deque dq; + dq.push_back(2); + dq.push_front(1); + dq.push_back(3); + dq.push_front(0); + if (dq.size() != 4) + return false; + if (dq[0] != 0 || dq[1] != 1 || dq[2] != 2 || dq[3] != 3) + return false; + return true; +} +static_assert(test_mixed_push()); + +// --- Test 11: copy assignment --- +constexpr bool test_copy_assign() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2; + dq2.push_back(99); + dq2 = dq; + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + return true; +} +static_assert(test_copy_assign()); + +// --- Test 12: move assignment --- +constexpr bool test_move_assign() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2; + dq2.push_back(99); + dq2 = ::std::move(dq); + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + return true; +} +static_assert(test_move_assign()); + +// --- Test 13: swap --- +constexpr bool test_swap() +{ + ::fast_io::deque dq1; + dq1.push_back(1); + dq1.push_back(2); + ::fast_io::deque dq2; + dq2.push_back(10); + dq2.push_back(20); + dq2.push_back(30); + swap(dq1, dq2); + if (dq1.size() != 3 || dq2.size() != 2) + return false; + if (dq1[0] != 10 || dq1[1] != 20 || dq1[2] != 30) + return false; + if (dq2[0] != 1 || dq2[1] != 2) + return false; + return true; +} +static_assert(test_swap()); + +// --- Test 14: iterator operations --- +constexpr bool test_iterators() +{ + ::fast_io::deque dq; + dq.push_back(10); + dq.push_back(20); + dq.push_back(30); + auto it = dq.begin(); + if (*it != 10) + return false; + ++it; + if (*it != 20) + return false; + it += 1; + if (*it != 30) + return false; + --it; + if (*it != 20) + return false; + if (dq.end() - dq.begin() != 3) + return false; + auto rit = dq.rbegin(); + if (*rit != 30) + return false; + return true; +} +static_assert(test_iterators()); + +} // namespace + +#endif + +int main() +{ + return 0; +}