diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv index 229c16065939f..3b7c138e4bd81 100644 --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -231,7 +231,7 @@ "`LWG3300 `__","Non-array ``ssize``\ overload is underconstrained","2020-02 (Prague)","|Nothing To Do|","","`#103929 `__","" "`LWG3301 `__","``transform_view::iterator``\ has incorrect ``iterator_category``\ ","2020-02 (Prague)","|Complete|","15","`#103932 `__","" "`LWG3302 `__","Range adaptor objects ``keys``\ and ``values``\ are unspecified","2020-02 (Prague)","|Complete|","16","`#103933 `__","" -"`LWG3303 `__","Bad ""``constexpr``\ "" marker for ``destroy/destroy_n``\ ","2020-02 (Prague)","","","`#101693 `__","" +"`LWG3303 `__","Bad ""``constexpr``\ "" marker for ``destroy/destroy_n``\ ","2020-02 (Prague)","|Complete|","24","`#101693 `__","" "`LWG3304 `__","Allocate functions of ``std::polymorphic_allocator``\ should require ``[[nodiscard]]``\ ","2020-02 (Prague)","|Complete|","16","`#103934 `__","" "`LWG3307 `__","``std::allocator().allocate(n)``\ ","2020-02 (Prague)","|Complete|","20","`#100422 `__","" "`LWG3310 `__","Replace ``SIZE_MAX``\ with ``numeric_limits::max()``\ ","2020-02 (Prague)","|Complete|","16","`#103936 `__","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 98b09b9dcf8ae..bbd742b8677bf 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -619,6 +619,7 @@ set(files __memory/noexcept_move_assign_container.h __memory/out_ptr.h __memory/pointer_traits.h + __memory/pstl.h __memory/ranges_construct_at.h __memory/ranges_destroy.h __memory/ranges_uninitialized_algorithms.h diff --git a/libcxx/include/__memory/pstl.h b/libcxx/include/__memory/pstl.h new file mode 100644 index 0000000000000..a4fc727f3bf72 --- /dev/null +++ b/libcxx/include/__memory/pstl.h @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___MEMORY_PSTL_H +#define _LIBCPP___MEMORY_PSTL_H + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +#if _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17 + +# include <__iterator/cpp17_iterator_concepts.h> +# include <__iterator/iterator_traits.h> +# include <__pstl/backend.h> +# include <__pstl/dispatch.h> +# include <__pstl/handle_exception.h> +# include <__type_traits/enable_if.h> +# include <__type_traits/is_execution_policy.h> +# include <__type_traits/remove_cvref.h> +# include <__utility/forward.h> +# include <__utility/move.h> + +_LIBCPP_BEGIN_NAMESPACE_STD + +template , + enable_if_t, int> = 0> +_LIBCPP_HIDE_FROM_ABI void destroy(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) { + _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "destroy requires ForwardIterators"); + using _Implementation = __pstl::__dispatch<__pstl::__destroy, __pstl::__current_configuration, _RawPolicy>; + __pstl::__handle_exception<_Implementation>( + std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last)); +} + +template , + enable_if_t, int> = 0> +_LIBCPP_HIDE_FROM_ABI void destroy_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n) { + _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "destroy_n requires ForwardIterators"); + using _Implementation = __pstl::__dispatch<__pstl::__destroy_n, __pstl::__current_configuration, _RawPolicy>; + __pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n); +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17 + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___MEMORY_PSTL_H diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h index e83375d1c0c74..c5a219fb59db2 100644 --- a/libcxx/include/__pstl/backend_fwd.h +++ b/libcxx/include/__pstl/backend_fwd.h @@ -365,6 +365,18 @@ struct __lexicographical_compare; // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, // _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Comp __comp) const noexcept; +template +struct __destroy; +// template +// optional<__empty> +// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept; + +template +struct __destroy_n; +// template +// optional<__empty> +// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept; + } // namespace __pstl _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h index 5562c23bf09f1..43bac357efd95 100644 --- a/libcxx/include/__pstl/backends/default.h +++ b/libcxx/include/__pstl/backends/default.h @@ -27,6 +27,8 @@ #include <__iterator/next.h> #include <__iterator/prev.h> #include <__iterator/reverse_iterator.h> +#include <__memory/addressof.h> +#include <__memory/construct_at.h> #include <__optional/comparison.h> #include <__optional/nullopt_t.h> #include <__optional/optional.h> @@ -78,6 +80,8 @@ namespace __pstl { // // for_each family // --------------- +// - destroy +// - destroy_n // - for_each_n // - fill // - fill_n @@ -368,6 +372,31 @@ struct __is_sorted<__default_backend_tag, _ExecutionPolicy> { ////////////////////////////////////////////////////////////// // for_each family ////////////////////////////////////////////////////////////// + +template +struct __destroy<__default_backend_tag, _ExecutionPolicy> { + template + optional<__empty> operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept { + using _ForEach = __dispatch<__for_each, __current_configuration, _ExecutionPolicy>; + using _Ref = __iterator_reference<_ForwardIterator>; + return _ForEach()(__policy, std::move(__first), std::move(__last), [&](_Ref __element) { + std::destroy_at(std::addressof(__element)); + }); + } +}; + +template +struct __destroy_n<__default_backend_tag, _ExecutionPolicy> { + template + optional<__empty> operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept { + using _ForEachN = __dispatch<__for_each_n, __current_configuration, _ExecutionPolicy>; + using _Ref = __iterator_reference<_ForwardIterator>; + return _ForEachN()(__policy, std::move(__first), __n, [&](_Ref __element) { + std::destroy_at(std::addressof(__element)); + }); + } +}; + template struct __for_each_n<__default_backend_tag, _ExecutionPolicy> { template diff --git a/libcxx/include/memory b/libcxx/include/memory index 65765c948b96c..e1e2e801a07ab 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -994,6 +994,7 @@ template # if _LIBCPP_STD_VER >= 17 # include <__memory/construct_at.h> # include <__memory/destroy.h> +# include <__memory/pstl.h> # endif # if _LIBCPP_STD_VER >= 20 diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 41c1ac1595234..92260ac3d9193 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1698,6 +1698,7 @@ module std { module noexcept_move_assign_container { header "__memory/noexcept_move_assign_container.h" } module out_ptr { header "__memory/out_ptr.h" } module pointer_traits { header "__memory/pointer_traits.h" } + module pstl { header "__memory/pstl.h" } module ranges_construct_at { header "__memory/ranges_construct_at.h" } module ranges_destroy { header "__memory/ranges_destroy.h" } module ranges_uninitialized_algorithms { diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp index e61c562025fa0..2d510be58e164 100644 --- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp +++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "test_iterators.h" @@ -70,6 +71,11 @@ void f(non_forward_iterator non_fwd, (void)std::count_if(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: count_if}} } + { + (void)std::destroy(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: destroy}} + (void)std::destroy_n(pol, non_fwd, n); // expected-error@*:* {{static assertion failed: destroy_n}} + } + { (void)std::equal(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: equal}} (void)std::equal(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: equal}} diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv index e12ca042800ab..e0a92259202c4 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -527,11 +527,17 @@ map stdexcept map tuple map version mdspan version +memory cctype +memory climits memory compare memory cstdint memory cstring +memory ctime +memory cwchar +memory cwctype memory initializer_list memory limits +memory ratio memory tuple memory typeinfo memory version diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv index 29499c037e5c2..44b5fe1f1577f 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -592,11 +592,17 @@ map stdexcept map tuple map version mdspan version +memory cctype +memory climits memory compare memory cstdint memory cstring +memory ctime +memory cwchar +memory cwctype memory initializer_list memory limits +memory ratio memory tuple memory typeinfo memory version diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv index 6658fa9c641a4..a199c610e9f57 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -626,11 +626,17 @@ mdspan limits mdspan span mdspan stdexcept mdspan version +memory cctype +memory climits memory compare memory cstdint memory cstring +memory ctime +memory cwchar +memory cwctype memory initializer_list memory limits +memory ratio memory tuple memory typeinfo memory version diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv index caf94dc4b2ca5..c272361cceb10 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -619,11 +619,17 @@ mdspan limits mdspan span mdspan stdexcept mdspan version +memory cctype +memory climits memory compare memory cstdint memory cstring +memory ctime +memory cwchar +memory cwctype memory initializer_list memory limits +memory ratio memory tuple memory typeinfo memory version diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp index 063210a13af7e..19e8cb9f8b0d1 100644 --- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp +++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp @@ -21,6 +21,7 @@ #include #include +#include #include "check_assertion.h" #include "test_execution_policies.h" @@ -127,6 +128,14 @@ int main(int, char**) { assert_non_throwing([=, &policy] { (void)std::count_if(policy, std::move(first1), std::move(last1), pred); }); } + { + // destroy(first, last) + assert_non_throwing([=, &policy] { (void)std::destroy(policy, std::move(first1), std::move(last1)); }); + + // destroy_n(first, n) + assert_non_throwing([=, &policy] { (void)std::destroy_n(policy, std::move(first1), n); }); + } + { auto binary_pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; }); diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy.pass.cpp new file mode 100644 index 0000000000000..f36a404bb9797 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy.pass.cpp @@ -0,0 +1,150 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++17 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +// template +// void destroy(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_execution_policies.h" +#include "test_iterators.h" +#include "test_macros.h" +#include "type_algorithms.h" +#include "runway_sample.h" + +EXECUTION_POLICY_SFINAE_TEST(destroy); + +static_assert(sfinae_test_destroy); +static_assert(!sfinae_test_destroy); + +struct Counted { + std::atomic_int* counter_; + Counted(std::atomic_int* counter) : counter_(counter) { counter_->fetch_add(1); } + Counted(Counted const& other) : counter_(other.counter_) { counter_->fetch_add(1); } + ~Counted() { counter_->fetch_sub(1); } + friend void operator&(Counted) = delete; +}; + +template +struct TestCounted { + template + void operator()(ExecutionPolicy&& policy) { + { + // Test destroy() with a range of Counted objects. + // Ranges vary in size from 0 to 1073. + // Each object has its own counter. + std::atomic_int counters[1073]; + std::fill_n(std::begin(counters), std::size(counters), 0); + + using Alloc = std::allocator; + Alloc alloc; + Counted* pool = std::allocator_traits::allocate(alloc, std::size(counters)); + + runway_sample(std::size(counters) + 1, [&](size_t size) { + for (std::size_t i = 0; i < size; ++i) { + std::allocator_traits::construct(alloc, std::addressof(pool[i]), &counters[i]); + } + assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; })); + + std::destroy(policy, Iter(pool), Iter(pool + size)); + ASSERT_SAME_TYPE(decltype(std::destroy(policy, Iter(pool), Iter(pool + size))), void); + assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 0; })); + }); + + std::allocator_traits::deallocate(alloc, pool, std::size(counters)); + } + } +}; + +// std::destroy on a sequence of arrays is supported since C++20. +#if TEST_STD_VER >= 20 +template +struct TestArrayCounted3 { + template + void operator()(ExecutionPolicy&& policy) { + { + // Test destroy() with a range of 5 Counted[3] objects. + // A shared counter is used for all objects. + using Array = Counted[3]; + using Alloc = std::allocator; + std::atomic_int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + std::allocator_traits::construct(alloc, std::addressof(arr[i]), &counter); + } + } + assert(counter == 5 * 3); + + std::destroy(policy, Iter(pool), Iter(pool + 5)); + ASSERT_SAME_TYPE(decltype(std::destroy(policy, Iter(pool), Iter(pool + 5))), void); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + } +}; + +template +struct TestArrayCounted3x2 { + template + void operator()(ExecutionPolicy&& policy) { + { + // Test destroy() with a range of 5 Counted[3][2] objects. + // A shared counter is used for all objects. + using Array = Counted[3][2]; + using Alloc = std::allocator; + std::atomic_int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + for (int j = 0; j != 2; ++j) { + std::allocator_traits::construct(alloc, std::addressof(arr[i][j]), &counter); + } + } + } + assert(counter == 5 * 3 * 2); + + std::destroy(policy, Iter(pool), Iter(pool + 5)); + ASSERT_SAME_TYPE(decltype(std::destroy(policy, Iter(pool), Iter(pool + 5))), void); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + } +}; +#endif // TEST_STD_VER >= 20 + +int main(int, char**) { + types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); +#if TEST_STD_VER >= 20 + using CountedArray3 = Counted[3]; + types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); + using CountedArray3x2 = Counted[3][2]; + types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); +#endif // TEST_STD_VER >= 20 + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy_n.pass.cpp new file mode 100644 index 0000000000000..bcbdb65bb0e2f --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy_n.pass.cpp @@ -0,0 +1,150 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++17 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +// template +// void destroy_n(ExecutionPolicy&& exec, ForwardIterator first, Size n); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_execution_policies.h" +#include "test_iterators.h" +#include "test_macros.h" +#include "type_algorithms.h" +#include "runway_sample.h" + +EXECUTION_POLICY_SFINAE_TEST(destroy_n); + +static_assert(sfinae_test_destroy_n); +static_assert(!sfinae_test_destroy_n); + +struct Counted { + std::atomic_int* counter_; + Counted(std::atomic_int* counter) : counter_(counter) { counter_->fetch_add(1); } + Counted(Counted const& other) : counter_(other.counter_) { counter_->fetch_add(1); } + ~Counted() { counter_->fetch_sub(1); } + friend void operator&(Counted) = delete; +}; + +template +struct TestCounted { + template + void operator()(ExecutionPolicy&& policy) { + { + // Test destroy_n() with a range of Counted objects. + // Ranges vary in size from 0 to 1073. + // Each object has its own counter. + std::atomic_int counters[1073]; + std::fill_n(std::begin(counters), std::size(counters), 0); + + using Alloc = std::allocator; + Alloc alloc; + Counted* pool = std::allocator_traits::allocate(alloc, std::size(counters)); + + runway_sample(std::size(counters) + 1, [&](size_t size) { + for (std::size_t i = 0; i < size; ++i) { + std::allocator_traits::construct(alloc, std::addressof(pool[i]), &counters[i]); + } + assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; })); + + std::destroy_n(policy, Iter(pool), size); + ASSERT_SAME_TYPE(decltype(std::destroy_n(policy, Iter(pool), size)), void); + assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 0; })); + }); + + std::allocator_traits::deallocate(alloc, pool, std::size(counters)); + } + } +}; + +// std::destroy_n on a sequence of arrays is supported since C++20. +#if TEST_STD_VER >= 20 +template +struct TestArrayCounted3 { + template + void operator()(ExecutionPolicy&& policy) { + { + // Test destroy_n() with a range of 5 Counted[3] objects. + // A shared counter is used for all objects. + using Array = Counted[3]; + using Alloc = std::allocator; + std::atomic_int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + std::allocator_traits::construct(alloc, std::addressof(arr[i]), &counter); + } + } + assert(counter == 5 * 3); + + std::destroy_n(policy, Iter(pool), 5); + ASSERT_SAME_TYPE(decltype(std::destroy_n(policy, Iter(pool), 5)), void); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + } +}; + +template +struct TestArrayCounted3x2 { + template + void operator()(ExecutionPolicy&& policy) { + { + // Test destroy_n() with a range of 5 Counted[3][2] objects. + // A shared counter is used for all objects. + using Array = Counted[3][2]; + using Alloc = std::allocator; + std::atomic_int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + for (int j = 0; j != 2; ++j) { + std::allocator_traits::construct(alloc, std::addressof(arr[i][j]), &counter); + } + } + } + assert(counter == 5 * 3 * 2); + std::destroy_n(policy, Iter(pool), 5); + ASSERT_SAME_TYPE(decltype(std::destroy_n(policy, Iter(pool), 5)), void); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + } +}; +#endif // TEST_STD_VER >= 20 + +int main(int, char**) { + types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); +#if TEST_STD_VER >= 20 + using CountedArray3 = Counted[3]; + types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); + using CountedArray3x2 = Counted[3][2]; + types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); +#endif // TEST_STD_VER >= 20 + return 0; +}