Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions libcxx/include/__algorithm/copy.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,7 @@ struct __copy_impl {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
operator()(_InIter __first, _InIter __last, _OutIter __result) const {
using _Traits = __segmented_iterator_traits<_OutIter>;
using _DiffT =
typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
using _DiffT = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;

if (__first == __last)
return std::make_pair(std::move(__first), std::move(__result));
Expand Down
3 changes: 1 addition & 2 deletions libcxx/include/__algorithm/copy_backward.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,7 @@ struct __copy_backward_impl {

auto __local_last = _Traits::__local(__result);
while (true) {
using _DiffT =
typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
using _DiffT = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;

auto __local_first = _Traits::__begin(__segment_iterator);
auto __size = std::min<_DiffT>(__local_last - __local_first, __last - __first);
Expand Down
15 changes: 7 additions & 8 deletions libcxx/include/__algorithm/count.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#ifndef _LIBCPP___ALGORITHM_COUNT_H
#define _LIBCPP___ALGORITHM_COUNT_H

#include <__algorithm/iterator_operations.h>
#include <__algorithm/min.h>
#include <__bit/invert_if.h>
#include <__bit/popcount.h>
Expand All @@ -31,10 +30,10 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD

// generic implementation
template <class _AlgPolicy, class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename _IterOps<_AlgPolicy>::template __difference_type<_Iter>
template <class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iter_difference_t<_Iter>
__count(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
typename _IterOps<_AlgPolicy>::template __difference_type<_Iter> __r(0);
__iter_difference_t<_Iter> __r(0);
for (; __first != __last; ++__first)
if (std::__invoke(__proj, *__first) == __value)
++__r;
Expand Down Expand Up @@ -71,8 +70,8 @@ __count_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_t
return __r;
}

template <class, class _Cp, bool _IsConst, class _Tp, class _Proj, __enable_if_t<__is_identity<_Proj>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iterator_difference_type<__bit_iterator<_Cp, _IsConst> >
template <class _Cp, bool _IsConst, class _Tp, class _Proj, __enable_if_t<__is_identity<_Proj>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iter_difference_t<__bit_iterator<_Cp, _IsConst> >
__count(__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, const _Tp& __value, _Proj&) {
if (__value)
return std::__count_bool<true>(
Expand All @@ -82,10 +81,10 @@ __count(__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __l
}

template <class _InputIterator, class _Tp>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iterator_difference_type<_InputIterator>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iter_difference_t<_InputIterator>
count(_InputIterator __first, _InputIterator __last, const _Tp& __value) {
__identity __proj;
return std::__count<_ClassicAlgPolicy>(__first, __last, __value, __proj);
return std::__count(__first, __last, __value, __proj);
}

_LIBCPP_END_NAMESPACE_STD
Expand Down
9 changes: 4 additions & 5 deletions libcxx/include/__algorithm/count_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#ifndef _LIBCPP___ALGORITHM_COUNT_IF_H
#define _LIBCPP___ALGORITHM_COUNT_IF_H

#include <__algorithm/iterator_operations.h>
#include <__config>
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
Expand All @@ -22,10 +21,10 @@

_LIBCPP_BEGIN_NAMESPACE_STD

template <class _AlgPolicy, class _Iter, class _Sent, class _Proj, class _Pred>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __policy_iter_diff_t<_AlgPolicy, _Iter>
template <class _Iter, class _Sent, class _Proj, class _Pred>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __iter_difference_t<_Iter>
__count_if(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
__policy_iter_diff_t<_AlgPolicy, _Iter> __counter(0);
__iter_difference_t<_Iter> __counter(0);
for (; __first != __last; ++__first) {
if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
++__counter;
Expand All @@ -38,7 +37,7 @@ template <class _InputIterator, class _Predicate>
typename iterator_traits<_InputIterator>::difference_type
count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
__identity __proj;
return std::__count_if<_ClassicAlgPolicy>(__first, __last, __pred, __proj);
return std::__count_if(__first, __last, __pred, __proj);
}

_LIBCPP_END_NAMESPACE_STD
Expand Down
8 changes: 4 additions & 4 deletions libcxx/include/__algorithm/is_permutation.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation_impl(
_Pred&& __pred,
_Proj1&& __proj1,
_Proj2&& __proj2) {
using _D1 = __iterator_difference_type<_Iter1>;
using _D1 = __iter_difference_t<_Iter1>;

for (auto __i = __first1; __i != __last1; ++__i) {
// Have we already counted the number of *__i in [f1, l1)?
Expand Down Expand Up @@ -126,7 +126,7 @@ template <class _AlgPolicy, class _ForwardIterator1, class _Sentinel1, class _Fo
return true;

// __first1 != __last1 && *__first1 != *__first2
using _D1 = __iterator_difference_type<_ForwardIterator1>;
using _D1 = __iter_difference_t<_ForwardIterator1>;
_D1 __l1 = _IterOps<_AlgPolicy>::distance(__first1, __last1);
if (__l1 == _D1(1))
return false;
Expand Down Expand Up @@ -173,10 +173,10 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
if (__first2 == __last2) // Second range is shorter
return false;

using _D1 = __iterator_difference_type<_Iter1>;
using _D1 = __iter_difference_t<_Iter1>;
_D1 __l1 = _IterOps<_AlgPolicy>::distance(__first1, __last1);

using _D2 = __iterator_difference_type<_Iter2>;
using _D2 = __iter_difference_t<_Iter2>;
_D2 __l2 = _IterOps<_AlgPolicy>::distance(__first2, __last2);
if (__l1 != __l2)
return false;
Expand Down
35 changes: 10 additions & 25 deletions libcxx/include/__algorithm/iterator_operations.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,9 @@ struct _RangeAlgPolicy {};

template <>
struct _IterOps<_RangeAlgPolicy> {
template <class _Iter>
using __value_type _LIBCPP_NODEBUG = iter_value_t<_Iter>;

template <class _Iter>
using __iterator_category _LIBCPP_NODEBUG = ranges::__iterator_concept<_Iter>;

template <class _Iter>
using __difference_type _LIBCPP_NODEBUG = iter_difference_t<_Iter>;

static constexpr auto advance = ranges::advance;
static constexpr auto distance = ranges::distance;
static constexpr auto __iter_move = ranges::iter_move;
Expand All @@ -71,15 +65,9 @@ struct _ClassicAlgPolicy {};

template <>
struct _IterOps<_ClassicAlgPolicy> {
template <class _Iter>
using __value_type _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::value_type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it okay to make this (general) change? I understand that we are probably just accepting more code than previously, but it is possible for conforming C++20 code to break when we do this?

A few questions:

  • In C++20, is it possible for an iterator to have different types for std::iter_value_t<It> and std::iterator_traits<It>::value_type?
  • If so, is it possible for these two types to differ to the extent that a classic algorithm (which should in principle use iterator_traits, or at least did historically) would break if we start using std::iter_value_t<It> instead?
  • If it's possible for these two typedefs to differ and to the extent of code breaking, are these cases so far fetched that we don't care to support them?
  • The same set of questions holds for every nested typedef we're modifying here.

Also CC @frederick-vs-ja since I think I remember having that discussion in a previous patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your proofs:

  1. For iter_reference_t, https://eel.is/c++draft/iterator.cpp17#input.iterators-2 says that *a must be iterator_traits::reference for a C++17 iterator. Hence iterator_traits::reference and decltype(*a) == iter_reference_t<It> must be the same. ✅

  2. For iter_difference_t, we have two cases. If iterator_traits<It> is specialized, incrementable.traits says that iter_difference_t<It> is iterator_traits<It>::difference_type. If not specialized, then iter_difference_t<It> is incrementable_traits<It>::difference_type, whatever that is. However, in C++20, iterator_traits also changed to be more C++20 friendly (I guess):

    • if It::difference_type and It::value_type and It::reference (etc..) do exist, then iterator_traits<It>::difference_type is It::difference_type. However, in that case, incrementable_traits<It>::difference_type is also It::difference_type, unless it is specialized explicitly. And it's legal to specialize it explicitly. This means that technically, if someone specializes incrementable_traits<It> to give it a difference_type that doesn't match iterator_traits<It>::difference_type, this patch will produce a difference. It's unclear whether

      Users may specialize incrementable_traits on program-defined types.

      is intended to override the normal requirement that specializations must be equivalent to the base template. This is probably material for a quick LWG issue. ❌

    • if It::difference_type or any of its friends do not exist, iterator_traits<It>::difference_type is defined to be incrementable_traits<It>::difference_type, so the equivalence holds. ✅

  3. For iter_value_t, iter_value_t<It> is iterator_traits<It>::value_type if iterator_traits<It> is specialized. Otherwise, it is indirectly_readable_traits<It>::value_type, whatever that is. However,

    • if It::difference_type and It::value_type and It::reference (etc..) do exist, then iterator_traits<It>::value_type is I::value_type. And in that case indirectly_readable_traits<It>::value_type is a rather complicated thing. It seems that if It::element_type and It::value_type disagree, indirectly_readable_traits<It>::value_type would not be defined whereas iterator_traits<It>::value_type would be. There's also the specialization problem above. And there's the issue of legacy output iterators where It::value_type == void, as explained in this note. ❌
    • if It::value_type or any of its friends do not exist, iterator_traits<It>::value_type is defined to indirectly_readable_traits<It>::value_type. ✅

So, in summary, I think we have the following issues:

  • iterator_traits<It>::difference_type may be It::difference_type but incrementable_traits<It> may be something else via a specialization
  • iterator_traits<It>::value_type may be It::value_type but indirectly_readable_traits<It>::value_type does not exist
  • iterator_traits<It>::value_type may be It::value_type but indirectly_readable_traits<It> may be specialized
  • iterator_traits<It>::value_type may be It::value_type == void but indirectly_readable_traits<It> does not exist

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, the question is whether we actually care about these differences.

I think we don't care about the specialization stuff. We should get those clarified with a LWG issue, but I have no sympathy for someone who has different It::difference_type and incrementable_traits<It>::difference_type through a specialization. Same for It::value_type.

However, the case of legacy output iterators could be a real problem. However, we basically never use iterator_traits<It>::value_type for an output iterator since it may be void. So in practice this might not matter.

I'll wait for @frederick-vs-ja to chime in, but so far I think I'd be comfortable with making this change. However, this would have to be dry-run on large code bases (we can ping the Google folks and we'll also do an internal build when we're ready), to shake off any unintended consequences.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I submitted LWG4080. Perhaps we should add some discussions to it.

  • It's unclear whether

    Users may specialize incrementable_traits on program-defined types.

    is intended to override the normal requirement that specializations must be equivalent to the base template.

I think overriding was intended. The standard library contains such design for common_iterator, although this looks quite questionable to me. If overriding is considered harmful (as it can create inconsistent value types), I think it makes more sense to disallow some user-provided specializations (and slightly fix common_iterator).

  • iterator_traits<It>::value_type may be It::value_type but indirectly_readable_traits<It>::value_type does not exist

I'm more worried about this. There can be C++17 iterators with evil element_type (I initially reported this in microsoft/STL#4436) which make them not C++20 iterators. If we use __iter_value_t like MSVC STL's current approach, some code using legacy algorithms can be accepted in C++17 but rejected in C++20.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really not that worried about a value_type/element_type mismatch. That rather seems to me like something worth diagnosing, since it's almost certainly user error. I'm not sure it's actually specified anywhere, but the intention that remove_reference_t<decltype(*iter)> is element_type seems rather clear to me.


template <class _Iter>
using __iterator_category _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::iterator_category;

template <class _Iter>
using __difference_type _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::difference_type;

// advance
template <class _Iter, class _Distance>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static void advance(_Iter& __iter, _Distance __count) {
Expand Down Expand Up @@ -164,30 +152,30 @@ struct _IterOps<_ClassicAlgPolicy> {

// advance with sentinel, a la std::ranges::advance
template <class _Iter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_Iter>
__advance_to(_Iter& __iter, __difference_type<_Iter> __count, const _Iter& __sentinel) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_Iter>
__advance_to(_Iter& __iter, __iter_difference_t<_Iter> __count, const _Iter& __sentinel) {
return _IterOps::__advance_to(__iter, __count, __sentinel, typename iterator_traits<_Iter>::iterator_category());
}

private:
// advance with sentinel, a la std::ranges::advance -- InputIterator specialization
template <class _InputIter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_InputIter> __advance_to(
_InputIter& __iter, __difference_type<_InputIter> __count, const _InputIter& __sentinel, input_iterator_tag) {
__difference_type<_InputIter> __dist = 0;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_InputIter> __advance_to(
_InputIter& __iter, __iter_difference_t<_InputIter> __count, const _InputIter& __sentinel, input_iterator_tag) {
__iter_difference_t<_InputIter> __dist = 0;
for (; __dist < __count && __iter != __sentinel; ++__dist)
++__iter;
return __count - __dist;
}

// advance with sentinel, a la std::ranges::advance -- BidirectionalIterator specialization
template <class _BiDirIter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_BiDirIter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_BiDirIter>
__advance_to(_BiDirIter& __iter,
__difference_type<_BiDirIter> __count,
__iter_difference_t<_BiDirIter> __count,
const _BiDirIter& __sentinel,
bidirectional_iterator_tag) {
__difference_type<_BiDirIter> __dist = 0;
__iter_difference_t<_BiDirIter> __dist = 0;
if (__count >= 0)
for (; __dist < __count && __iter != __sentinel; ++__dist)
++__iter;
Expand All @@ -199,9 +187,9 @@ struct _IterOps<_ClassicAlgPolicy> {

// advance with sentinel, a la std::ranges::advance -- RandomIterator specialization
template <class _RandIter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_RandIter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_RandIter>
__advance_to(_RandIter& __iter,
__difference_type<_RandIter> __count,
__iter_difference_t<_RandIter> __count,
const _RandIter& __sentinel,
random_access_iterator_tag) {
auto __dist = _IterOps::distance(__iter, __sentinel);
Expand All @@ -216,9 +204,6 @@ struct _IterOps<_ClassicAlgPolicy> {
}
};

template <class _AlgPolicy, class _Iter>
using __policy_iter_diff_t _LIBCPP_NODEBUG = typename _IterOps<_AlgPolicy>::template __difference_type<_Iter>;

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ template <class _InputIterator1, class _InputIterator2, class _Cmp>
_LIBCPP_HIDE_FROM_ABI constexpr auto __lexicographical_compare_three_way_fast_path(
_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2, _Cmp& __comp)
-> decltype(__comp(*__first1, *__first2)) {
static_assert(signed_integral<__iterator_difference_type<_InputIterator1>>,
static_assert(signed_integral<__iter_difference_t<_InputIterator1>>,
"Using a non-integral difference_type is undefined behavior.");
static_assert(signed_integral<__iterator_difference_type<_InputIterator2>>,
static_assert(signed_integral<__iter_difference_t<_InputIterator2>>,
"Using a non-integral difference_type is undefined behavior.");

using _Len1 = __iterator_difference_type<_InputIterator1>;
using _Len2 = __iterator_difference_type<_InputIterator2>;
using _Len1 = __iter_difference_t<_InputIterator1>;
using _Len2 = __iter_difference_t<_InputIterator2>;
using _Common = common_type_t<_Len1, _Len2>;

_Len1 __len1 = __last1 - __first1;
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__algorithm/make_heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
__make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
__comp_ref_type<_Compare> __comp_ref = __comp;

using __diff_t = __iterator_difference_type<_RandomAccessIterator>;
using __diff_t = __iter_difference_t<_RandomAccessIterator>;
const __diff_t __n = __last - __first;

const bool __assume_both_children = is_arithmetic<__iterator_value_type<_RandomAccessIterator> >::value;
const bool __assume_both_children = is_arithmetic<__iter_value_t<_RandomAccessIterator> >::value;

// While it would be correct to always assume we have both children, in practice we observed this to be a performance
// improvement only for arithmetic types.
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__algorithm/mismatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ __mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Pred& __pred, _Pro
template <class _Iter>
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_Iter, _Iter>
__mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
using __value_type = __iterator_value_type<_Iter>;
using __value_type = __iter_value_t<_Iter>;
constexpr size_t __unroll_count = 4;
constexpr size_t __vec_size = __native_vector_size<__value_type>;
using __vec = __simd_vector<__value_type, __vec_size>;
Expand Down
3 changes: 1 addition & 2 deletions libcxx/include/__algorithm/move.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ struct __move_impl {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
operator()(_InIter __first, _InIter __last, _OutIter __result) const {
using _Traits = __segmented_iterator_traits<_OutIter>;
using _DiffT =
typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
using _DiffT = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;

if (__first == __last)
return std::make_pair(std::move(__first), std::move(__result));
Expand Down
3 changes: 1 addition & 2 deletions libcxx/include/__algorithm/move_backward.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ struct __move_backward_impl {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
operator()(_InIter __first, _InIter __last, _OutIter __result) const {
using _Traits = __segmented_iterator_traits<_OutIter>;
using _DiffT =
typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
using _DiffT = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;

// When the range contains no elements, __result might not be a valid iterator
if (__first == __last)
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__algorithm/pstl.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ template <class _ExecutionPolicy,
class _Predicate,
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
_LIBCPP_HIDE_FROM_ABI __iterator_difference_type<_ForwardIterator>
_LIBCPP_HIDE_FROM_ABI __iter_difference_t<_ForwardIterator>
count_if(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) {
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(
_ForwardIterator, "count_if(first, last, pred) requires [first, last) to be ForwardIterators");
Expand All @@ -129,7 +129,7 @@ template <class _ExecutionPolicy,
class _Tp,
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
_LIBCPP_HIDE_FROM_ABI __iterator_difference_type<_ForwardIterator>
_LIBCPP_HIDE_FROM_ABI __iter_difference_t<_ForwardIterator>
count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) {
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(
_ForwardIterator, "count(first, last, val) requires [first, last) to be ForwardIterators");
Expand Down
Loading
Loading