[libc++] Backport segmented iterator optimization for std::for_each to C++11 (#134960)

Previously, the segmented iterator optimization for `std::for_each` was restricted to C++23 and later due to its dependency on `__movable_box`, which is not available in earlier standards. This patch eliminates that restriction, enabling consistent optimizations starting from C++11. 

By backporting this enhancement, we improve performance across older standards and create opportunities to extend similar optimizations to other algorithms by forwarding their calls to `std::for_each`.
This commit is contained in:
Peng Liu
2025-04-19 07:12:43 -04:00
committed by GitHub
parent f91df0d58d
commit e9280a1d39
15 changed files with 277 additions and 163 deletions

View File

@@ -67,6 +67,9 @@ Improvements and New Features
- The ``std::stable_sort`` algorithm uses radix sort for floating-point types now, which can improve the performance
up to 10x, depending on type of sorted elements and the initial state of the sorted array.
- The segmented iterator optimization for ``std::for_each`` has been backported to C++11. Previously it was only available
in C++23 and later.
Deprecations and Removals
-------------------------

View File

@@ -13,44 +13,40 @@
#include <__algorithm/for_each_segment.h>
#include <__config>
#include <__iterator/segmented_iterator.h>
#include <__ranges/movable_box.h>
#include <__utility/in_place.h>
#include <__utility/move.h>
#include <__type_traits/enable_if.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _InputIterator, class _Sent, class _Func>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __for_each(_InputIterator __first, _Sent __last, _Func& __f) {
for (; __first != __last; ++__first)
__f(*__first);
}
#ifndef _LIBCPP_CXX03_LANG
template <class _SegmentedIterator,
class _Function,
__enable_if_t<__is_segmented_iterator<_SegmentedIterator>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function& __func) {
using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
std::__for_each(__lfirst, __llast, __func);
});
}
#endif // !_LIBCPP_CXX03_LANG
template <class _InputIterator, class _Function>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function
for_each(_InputIterator __first, _InputIterator __last, _Function __f) {
for (; __first != __last; ++__first)
__f(*__first);
std::__for_each(__first, __last, __f);
return __f;
}
// __movable_box is available in C++20, but is actually a copyable-box, so optimization is only correct in C++23
#if _LIBCPP_STD_VER >= 23
template <class _SegmentedIterator, class _Function>
requires __is_segmented_iterator<_SegmentedIterator>::value
_LIBCPP_HIDE_FROM_ABI constexpr _Function
for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function __func) {
ranges::__movable_box<_Function> __wrapped_func(in_place, std::move(__func));
std::__for_each_segment(__first, __last, [&](auto __lfirst, auto __llast) {
__wrapped_func =
ranges::__movable_box<_Function>(in_place, std::for_each(__lfirst, __llast, std::move(*__wrapped_func)));
});
return std::move(*__wrapped_func);
}
#endif // _LIBCPP_STD_VER >= 23
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // _LIBCPP___ALGORITHM_FOR_EACH_H

View File

@@ -2061,6 +2061,7 @@ template <class BidirectionalIterator, class Compare>
# include <cstring>
# include <iterator>
# include <memory>
# include <optional>
# include <stdexcept>
# include <type_traits>
# include <utility>

View File

@@ -566,6 +566,7 @@ _LIBCPP_POP_MACROS
# include <cstdlib>
# include <iterator>
# include <new>
# include <optional>
# include <type_traits>
# include <utility>
# endif

View File

@@ -973,6 +973,7 @@ _LIBCPP_POP_MACROS
# if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
# include <concepts>
# include <cstdlib>
# include <optional>
# include <type_traits>
# endif
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

View File

@@ -596,6 +596,7 @@ _LIBCPP_END_NAMESPACE_STD
# include <limits>
# include <mutex>
# include <new>
# include <optional>
# include <stdexcept>
# include <type_traits>
# include <typeinfo>

View File

@@ -357,6 +357,7 @@ _LIBCPP_POP_MACROS
# include <initializer_list>
# include <iosfwd>
# include <new>
# include <optional>
# include <stdexcept>
# include <system_error>
# include <type_traits>

View File

@@ -887,6 +887,7 @@ _LIBCPP_POP_MACROS
# include <limits>
# include <mutex>
# include <new>
# include <optional>
# include <stdexcept>
# include <system_error>
# include <type_traits>

View File

@@ -3692,6 +3692,7 @@ _LIBCPP_POP_MACROS
# include <cstdarg>
# include <iterator>
# include <mutex>
# include <optional>
# include <stdexcept>
# include <type_traits>
# include <typeinfo>

View File

@@ -386,6 +386,7 @@ _LIBCPP_POP_MACROS
# if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
# include <cstdint>
# include <optional>
# endif
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

View File

@@ -4027,6 +4027,7 @@ _LIBCPP_POP_MACROS
# include <cstdlib>
# include <iterator>
# include <new>
# include <optional>
# include <type_traits>
# include <typeinfo>
# include <utility>

View File

@@ -952,6 +952,7 @@ _LIBCPP_POP_MACROS
# include <concepts>
# include <cstdlib>
# include <iterator>
# include <optional>
# include <type_traits>
# endif
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

View File

@@ -168,6 +168,7 @@ template <> struct hash<std::error_condition>;
# include <cstdint>
# include <cstring>
# include <limits>
# include <optional>
# include <type_traits>
# endif
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

View File

@@ -362,6 +362,7 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
# if _LIBCPP_HAS_LOCALIZATION
# include <locale>
# endif
# include <optional>
# include <string>
# include <string_view>
# include <tuple>

View File

@@ -12,209 +12,312 @@
#include <cassert>
#include <compare>
#include <cstddef>
#include <deque>
#include <ranges>
#include <type_traits>
#include <vector>
#include "test_macros.h"
template <class T>
struct Less {
int *copies_;
TEST_CONSTEXPR explicit Less(int *copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 Less(const Less& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 Less& operator=(const Less&) = default;
TEST_CONSTEXPR bool operator()(T, T) const { return false; }
int* copies_;
TEST_CONSTEXPR explicit Less(int* copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 Less(const Less& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 Less& operator=(const Less&) = default;
TEST_CONSTEXPR bool operator()(T, T) const { return false; }
};
template <class T>
struct Equal {
int *copies_;
TEST_CONSTEXPR explicit Equal(int *copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 Equal(const Equal& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 Equal& operator=(const Equal&) = default;
TEST_CONSTEXPR bool operator()(T, T) const { return true; }
int* copies_;
TEST_CONSTEXPR explicit Equal(int* copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 Equal(const Equal& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 Equal& operator=(const Equal&) = default;
TEST_CONSTEXPR bool operator()(T, T) const { return true; }
};
template <class T>
struct UnaryVoid {
int *copies_;
TEST_CONSTEXPR explicit UnaryVoid(int *copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 UnaryVoid(const UnaryVoid& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 UnaryVoid& operator=(const UnaryVoid&) = default;
TEST_CONSTEXPR_CXX14 void operator()(T) const {}
int* copies_;
TEST_CONSTEXPR explicit UnaryVoid(int* copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 UnaryVoid(const UnaryVoid& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 UnaryVoid& operator=(const UnaryVoid&) = default;
TEST_CONSTEXPR_CXX14 void operator()(T) const {}
};
template <class T>
struct UnaryTrue {
int *copies_;
TEST_CONSTEXPR explicit UnaryTrue(int *copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 UnaryTrue(const UnaryTrue& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 UnaryTrue& operator=(const UnaryTrue&) = default;
TEST_CONSTEXPR bool operator()(T) const { return true; }
int* copies_;
TEST_CONSTEXPR explicit UnaryTrue(int* copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 UnaryTrue(const UnaryTrue& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 UnaryTrue& operator=(const UnaryTrue&) = default;
TEST_CONSTEXPR bool operator()(T) const { return true; }
};
template <class T>
struct NullaryValue {
int *copies_;
TEST_CONSTEXPR explicit NullaryValue(int *copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 NullaryValue(const NullaryValue& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 NullaryValue& operator=(const NullaryValue&) = default;
TEST_CONSTEXPR T operator()() const { return 0; }
int* copies_;
TEST_CONSTEXPR explicit NullaryValue(int* copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 NullaryValue(const NullaryValue& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 NullaryValue& operator=(const NullaryValue&) = default;
TEST_CONSTEXPR T operator()() const { return 0; }
};
template <class T>
struct UnaryTransform {
int *copies_;
TEST_CONSTEXPR explicit UnaryTransform(int *copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 UnaryTransform(const UnaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 UnaryTransform& operator=(const UnaryTransform&) = default;
TEST_CONSTEXPR T operator()(T) const { return 0; }
int* copies_;
TEST_CONSTEXPR explicit UnaryTransform(int* copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 UnaryTransform(const UnaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 UnaryTransform& operator=(const UnaryTransform&) = default;
TEST_CONSTEXPR T operator()(T) const { return 0; }
};
template <class T>
struct BinaryTransform {
int *copies_;
TEST_CONSTEXPR explicit BinaryTransform(int *copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 BinaryTransform(const BinaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 BinaryTransform& operator=(const BinaryTransform&) = default;
TEST_CONSTEXPR T operator()(T, T) const { return 0; }
int* copies_;
TEST_CONSTEXPR explicit BinaryTransform(int* copies) : copies_(copies) {}
TEST_CONSTEXPR_CXX14 BinaryTransform(const BinaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
TEST_CONSTEXPR_CXX14 BinaryTransform& operator=(const BinaryTransform&) = default;
TEST_CONSTEXPR T operator()(T, T) const { return 0; }
};
#if TEST_STD_VER > 17
template <class T>
struct ThreeWay {
int *copies_;
constexpr explicit ThreeWay(int *copies) : copies_(copies) {}
constexpr ThreeWay(const ThreeWay& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
constexpr ThreeWay& operator=(const ThreeWay&) = default;
constexpr std::strong_ordering operator()(T, T) const { return std::strong_ordering::equal; }
int* copies_;
constexpr explicit ThreeWay(int* copies) : copies_(copies) {}
constexpr ThreeWay(const ThreeWay& rhs) : copies_(rhs.copies_) { *copies_ += 1; }
constexpr ThreeWay& operator=(const ThreeWay&) = default;
constexpr std::strong_ordering operator()(T, T) const { return std::strong_ordering::equal; }
};
#endif
template <class T>
TEST_CONSTEXPR_CXX20 bool all_the_algorithms()
{
T a[10] = {};
T b[10] = {};
T *first = a;
T *mid = a+5;
T *last = a+10;
T *first2 = b;
T *mid2 = b+5;
T *last2 = b+10;
T value = 0;
int count = 1;
TEST_CONSTEXPR_CXX20 bool all_the_algorithms() {
T a[10] = {};
T b[10] = {};
T* first = a;
T* mid = a + 5;
T* last = a + 10;
T* first2 = b;
T* mid2 = b + 5;
T* last2 = b + 10;
T value = 0;
int count = 1;
int copies = 0;
(void)std::adjacent_find(first, last, Equal<T>(&copies)); assert(copies == 0);
int copies = 0;
(void)std::adjacent_find(first, last, Equal<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER >= 11
(void)std::all_of(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::any_of(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::all_of(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::any_of(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
#endif
(void)std::binary_search(first, last, value, Less<T>(&copies)); assert(copies == 0);
(void)std::binary_search(first, last, value, Less<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER > 17
(void)std::clamp(value, value, value, Less<T>(&copies)); assert(copies == 0);
(void)std::clamp(value, value, value, Less<T>(&copies));
assert(copies == 0);
#endif
(void)std::count_if(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::copy_if(first, last, first2, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::equal(first, last, first2, Equal<T>(&copies)); assert(copies == 0);
(void)std::count_if(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::copy_if(first, last, first2, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::equal(first, last, first2, Equal<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER > 11
(void)std::equal(first, last, first2, last2, Equal<T>(&copies)); assert(copies == 0);
(void)std::equal(first, last, first2, last2, Equal<T>(&copies));
assert(copies == 0);
#endif
(void)std::equal_range(first, last, value, Less<T>(&copies)); assert(copies == 0);
(void)std::find_end(first, last, first2, mid2, Equal<T>(&copies)); assert(copies == 0);
(void)std::find_first_of(first, last, first2, last2, Equal<T>(&copies)); assert(copies == 0);
(void)std::find_if(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::find_if_not(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::for_each(first, last, UnaryVoid<T>(&copies)); assert(copies == 1); copies = 0;
(void)std::equal_range(first, last, value, Less<T>(&copies));
assert(copies == 0);
(void)std::find_end(first, last, first2, mid2, Equal<T>(&copies));
assert(copies == 0);
(void)std::find_first_of(first, last, first2, last2, Equal<T>(&copies));
assert(copies == 0);
(void)std::find_if(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::find_if_not(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::for_each(first, last, UnaryVoid<T>(&copies));
assert(copies == 1);
copies = 0;
#if TEST_STD_VER > 14
(void)std::for_each_n(first, count, UnaryVoid<T>(&copies)); assert(copies == 0);
(void)std::for_each_n(first, count, UnaryVoid<T>(&copies));
assert(copies == 0);
#endif
(void)std::generate(first, last, NullaryValue<T>(&copies)); assert(copies == 0);
(void)std::generate_n(first, count, NullaryValue<T>(&copies)); assert(copies == 0);
(void)std::includes(first, last, first2, last2, Less<T>(&copies)); assert(copies == 0);
(void)std::is_heap(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::is_heap_until(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::is_partitioned(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::is_permutation(first, last, first2, Equal<T>(&copies)); assert(copies == 0);
(void)std::generate(first, last, NullaryValue<T>(&copies));
assert(copies == 0);
(void)std::generate_n(first, count, NullaryValue<T>(&copies));
assert(copies == 0);
(void)std::includes(first, last, first2, last2, Less<T>(&copies));
assert(copies == 0);
(void)std::is_heap(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::is_heap_until(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::is_partitioned(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::is_permutation(first, last, first2, Equal<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER > 11
(void)std::is_permutation(first, last, first2, last2, Equal<T>(&copies)); assert(copies == 0);
(void)std::is_permutation(first, last, first2, last2, Equal<T>(&copies));
assert(copies == 0);
#endif
(void)std::is_sorted(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::is_sorted_until(first, last, Less<T>(&copies)); assert(copies == 0);
if (!TEST_IS_CONSTANT_EVALUATED) { (void)std::inplace_merge(first, mid, last, Less<T>(&copies)); assert(copies == 0); }
(void)std::lexicographical_compare(first, last, first2, last2, Less<T>(&copies)); assert(copies == 0);
(void)std::is_sorted(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::is_sorted_until(first, last, Less<T>(&copies));
assert(copies == 0);
if (!TEST_IS_CONSTANT_EVALUATED) {
(void)std::inplace_merge(first, mid, last, Less<T>(&copies));
assert(copies == 0);
}
(void)std::lexicographical_compare(first, last, first2, last2, Less<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER > 17
(void)std::lexicographical_compare_three_way(first, last, first2, last2, ThreeWay<T>(&copies)); assert(copies == 0);
(void)std::lexicographical_compare_three_way(first, last, first2, last2, ThreeWay<T>(&copies));
assert(copies == 0);
#endif
(void)std::lower_bound(first, last, value, Less<T>(&copies)); assert(copies == 0);
(void)std::make_heap(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::max(value, value, Less<T>(&copies)); assert(copies == 0);
(void)std::lower_bound(first, last, value, Less<T>(&copies));
assert(copies == 0);
(void)std::make_heap(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::max(value, value, Less<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER >= 11
(void)std::max({ value, value }, Less<T>(&copies)); assert(copies == 0);
(void)std::max({value, value}, Less<T>(&copies));
assert(copies == 0);
#endif
(void)std::max_element(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::merge(first, mid, mid, last, first2, Less<T>(&copies)); assert(copies == 0);
(void)std::min(value, value, Less<T>(&copies)); assert(copies == 0);
(void)std::max_element(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::merge(first, mid, mid, last, first2, Less<T>(&copies));
assert(copies == 0);
(void)std::min(value, value, Less<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER >= 11
(void)std::min({ value, value }, Less<T>(&copies)); assert(copies == 0);
(void)std::min({value, value}, Less<T>(&copies));
assert(copies == 0);
#endif
(void)std::min_element(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::minmax(value, value, Less<T>(&copies)); assert(copies == 0);
(void)std::min_element(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::minmax(value, value, Less<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER >= 11
(void)std::minmax({ value, value }, Less<T>(&copies)); assert(copies == 0);
(void)std::minmax({value, value}, Less<T>(&copies));
assert(copies == 0);
#endif
(void)std::minmax_element(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::mismatch(first, last, first2, Equal<T>(&copies)); assert(copies == 0);
(void)std::minmax_element(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::mismatch(first, last, first2, Equal<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER > 11
(void)std::mismatch(first, last, first2, last2, Equal<T>(&copies)); assert(copies == 0);
(void)std::mismatch(first, last, first2, last2, Equal<T>(&copies));
assert(copies == 0);
#endif
(void)std::next_permutation(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::next_permutation(first, last, Less<T>(&copies));
assert(copies == 0);
#if TEST_STD_VER >= 11
(void)std::none_of(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::none_of(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
#endif
(void)std::nth_element(first, mid, last, Less<T>(&copies)); assert(copies == 0);
(void)std::partial_sort(first, mid, last, Less<T>(&copies)); assert(copies == 0);
(void)std::partial_sort_copy(first, last, first2, mid2, Less<T>(&copies)); assert(copies == 0);
(void)std::partition(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::partition_copy(first, last, first2, last2, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::partition_point(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::pop_heap(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::prev_permutation(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::push_heap(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::remove_copy_if(first, last, first2, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::remove_if(first, last, UnaryTrue<T>(&copies)); assert(copies == 0);
(void)std::replace_copy_if(first, last, first2, UnaryTrue<T>(&copies), value); assert(copies == 0);
(void)std::replace_if(first, last, UnaryTrue<T>(&copies), value); assert(copies == 0);
(void)std::search(first, last, first2, mid2, Equal<T>(&copies)); assert(copies == 0);
(void)std::search_n(first, last, count, value, Equal<T>(&copies)); assert(copies == 0);
(void)std::set_difference(first, mid, mid, last, first2, Less<T>(&copies)); assert(copies == 0);
(void)std::set_intersection(first, mid, mid, last, first2, Less<T>(&copies)); assert(copies == 0);
(void)std::set_symmetric_difference(first, mid, mid, last, first2, Less<T>(&copies)); assert(copies == 0);
(void)std::set_union(first, mid, mid, last, first2, Less<T>(&copies)); assert(copies == 0);
(void)std::sort(first, first+3, Less<T>(&copies)); assert(copies == 0);
(void)std::sort(first, first+4, Less<T>(&copies)); assert(copies == 0);
(void)std::sort(first, first+5, Less<T>(&copies)); assert(copies == 0);
(void)std::sort(first, last, Less<T>(&copies)); assert(copies == 0);
(void)std::sort_heap(first, last, Less<T>(&copies)); assert(copies == 0);
if (!TEST_IS_CONSTANT_EVALUATED) { (void)std::stable_partition(first, last, UnaryTrue<T>(&copies)); assert(copies == 0); }
if (!TEST_IS_CONSTANT_EVALUATED) { (void)std::stable_sort(first, last, Less<T>(&copies)); assert(copies == 0); }
(void)std::transform(first, last, first2, UnaryTransform<T>(&copies)); assert(copies == 0);
(void)std::transform(first, mid, mid, first2, BinaryTransform<T>(&copies)); assert(copies == 0);
(void)std::unique(first, last, Equal<T>(&copies)); assert(copies == 0);
(void)std::unique_copy(first, last, first2, Equal<T>(&copies)); assert(copies == 0);
(void)std::upper_bound(first, last, value, Less<T>(&copies)); assert(copies == 0);
(void)std::nth_element(first, mid, last, Less<T>(&copies));
assert(copies == 0);
(void)std::partial_sort(first, mid, last, Less<T>(&copies));
assert(copies == 0);
(void)std::partial_sort_copy(first, last, first2, mid2, Less<T>(&copies));
assert(copies == 0);
(void)std::partition(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::partition_copy(first, last, first2, last2, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::partition_point(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::pop_heap(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::prev_permutation(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::push_heap(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::remove_copy_if(first, last, first2, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::remove_if(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
(void)std::replace_copy_if(first, last, first2, UnaryTrue<T>(&copies), value);
assert(copies == 0);
(void)std::replace_if(first, last, UnaryTrue<T>(&copies), value);
assert(copies == 0);
(void)std::search(first, last, first2, mid2, Equal<T>(&copies));
assert(copies == 0);
(void)std::search_n(first, last, count, value, Equal<T>(&copies));
assert(copies == 0);
(void)std::set_difference(first, mid, mid, last, first2, Less<T>(&copies));
assert(copies == 0);
(void)std::set_intersection(first, mid, mid, last, first2, Less<T>(&copies));
assert(copies == 0);
(void)std::set_symmetric_difference(first, mid, mid, last, first2, Less<T>(&copies));
assert(copies == 0);
(void)std::set_union(first, mid, mid, last, first2, Less<T>(&copies));
assert(copies == 0);
(void)std::sort(first, first + 3, Less<T>(&copies));
assert(copies == 0);
(void)std::sort(first, first + 4, Less<T>(&copies));
assert(copies == 0);
(void)std::sort(first, first + 5, Less<T>(&copies));
assert(copies == 0);
(void)std::sort(first, last, Less<T>(&copies));
assert(copies == 0);
(void)std::sort_heap(first, last, Less<T>(&copies));
assert(copies == 0);
if (!TEST_IS_CONSTANT_EVALUATED) {
(void)std::stable_partition(first, last, UnaryTrue<T>(&copies));
assert(copies == 0);
}
if (!TEST_IS_CONSTANT_EVALUATED) {
(void)std::stable_sort(first, last, Less<T>(&copies));
assert(copies == 0);
}
(void)std::transform(first, last, first2, UnaryTransform<T>(&copies));
assert(copies == 0);
(void)std::transform(first, mid, mid, first2, BinaryTransform<T>(&copies));
assert(copies == 0);
(void)std::unique(first, last, Equal<T>(&copies));
assert(copies == 0);
(void)std::unique_copy(first, last, first2, Equal<T>(&copies));
assert(copies == 0);
(void)std::upper_bound(first, last, value, Less<T>(&copies));
assert(copies == 0);
return true;
return true;
}
int main(int, char**)
{
all_the_algorithms<void*>();
all_the_algorithms<int>();
#if TEST_STD_VER > 17
static_assert(all_the_algorithms<void*>());
static_assert(all_the_algorithms<int>());
bool test_segmented_iterator() {
int copies = 0;
std::deque<int> dq(10);
(void)std::for_each(dq.begin(), dq.end(), UnaryVoid<int>(&copies));
assert(copies == 1);
copies = 0;
#if TEST_STD_VER >= 20
std::vector<std::vector<int>> vecs(3, std::vector<int>(10));
auto v = std::views::join(vecs);
(void)std::for_each(v.begin(), v.end(), UnaryVoid<int>(&copies));
assert(copies == 1);
copies = 0;
#endif
return 0;
return true;
}
int main(int, char**) {
all_the_algorithms<void*>();
all_the_algorithms<int>();
assert(test_segmented_iterator());
#if TEST_STD_VER > 17
static_assert(all_the_algorithms<void*>());
static_assert(all_the_algorithms<int>());
#endif
return 0;
}