This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4297. Missing permutable constraint for iterator overloads in Parallel Range Algorithms

Section: 26.4 [algorithm.syn], 26.7.8 [alg.remove], 26.8.5 [alg.partitions] Status: New Submitter: Ruslan Arutyunyan Opened: 2025-06-27 Last modified: 2025-07-26

Priority: Not Prioritized

View other active issues in [algorithm.syn].

View all other issues in [algorithm.syn].

View all issues with New status.

Discussion:

The P3179R9: Parallel Range Algorithms paper was accepted to C++ working draft for C++ 26. Unfortunately, there is an oversight for three algorithms — remove, remove_if and partition — where the permutable constraint is missing. This applies to "Iterator and Sentinel" overloads only. The issue exists in 26.4 [algorithm.syn] as well as in per-algorithm sections: 26.8.5 [alg.partitions] and 26.7.8 [alg.remove].

Proposed resolution:

This wording is relative to this CD preview draft.

  1. Modify 26.4 [algorithm.syn], header <algorithm>, as indicated:

    […]
    template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
             class Proj = identity, class T = projected_value_t<I, Proj>>
      requires permutable<I> &&
               indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
      subrange<I> remove(Ep& exec, I first, S last, const T& value,
                         Proj proj = {}); // freestanding-deleted
    template<execution-policy Ep, sized-random-access-range R, class Proj = identity,
             class T = projected_value_t<iterator_t<R>, Proj>>
      requires permutable<iterator_t<R>> &&
               indirect_binary_predicate<ranges::equal_to,
                                         projected<iterator_t<R>, Proj>, const T*>
      borrowed_subrange_t<R>
        remove(Ep&& exec, R&& r, const T& value, Proj proj = {}); // freestanding-deleted                                     
    […]
    template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
             class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
      requires permutable<I>
      subrange<I>
        remove_if(Ep& exec, I first, S last, Pred pred, Proj proj = {}); // freestanding-deleted
    template<execution-policy Ep, sized-random-access-range R, class Proj = identity,
             indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
      requires permutable<iterator_t<R>>
      borrowed_subrange_t<R>
        remove_if(Ep& exec, R& r, Pred pred, Proj proj = {}); // freestanding-deleted
    […]
    template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
             class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
      requires permutable<I>
      subrange<I>
        partition(Ep&& exec, I first, S last, Pred pred, Proj proj = {}); // freestanding-deleted
    template<execution-policy Ep, sized-random-access-range R, class Proj = identity,
             indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
      requires permutable<iterator_t<R>>
      borrowed_subrange_t<R>
        partition(Ep&& exec, R&& r, Pred pred, Proj proj = {}); // freestanding-deleted
    […]
    
  2. Modify 26.7.8 [alg.remove] as indicated:

    […]
    template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
             class Proj = identity, class T = projected_value_t<I, Proj>>
      requires permutable<I> &&
               indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
      subrange<I> 
        ranges::remove(Ep& exec, I first, S last, const T& value, Proj proj = {});
    template<execution-policy Ep, sized-random-access-range R, class Proj = identity,
             class T = projected_value_t<iterator_t<R>, Proj>>
      requires permutable<iterator_t<R>> &&
               indirect_binary_predicate<ranges::equal_to,
                                         projected<iterator_t<R>, Proj>, const T*>
      borrowed_subrange_t<R>
        ranges::remove(Ep&& exec, R&& r, const T& value, Proj proj = {});                                  
    […]
    template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
             class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
      requires permutable<I>
      subrange<I>
        ranges::remove_if(Ep& exec, I first, S last, Pred pred, Proj proj = {});
    template<execution-policy Ep, sized-random-access-range R, class Proj = identity,
             indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
      requires permutable<iterator_t<R>>
      borrowed_subrange_t<R>
        ranges::remove_if(Ep& exec, R& r, Pred pred, Proj proj = {});
    

    -1- Let E be […]

  3. Modify 26.8.5 [alg.partitions] as indicated:

    […]
    template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
             class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
      requires permutable<I>
      subrange<I>
        ranges::partition(Ep&& exec, I first, S last, Pred pred, Proj proj = {});
    template<execution-policy Ep, sized-random-access-range R, class Proj = identity,
             indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
      requires permutable<iterator_t<R>>
      borrowed_subrange_t<R>
        ranges::partition(Ep&& exec, R&& r, Pred pred, Proj proj = {});
    

    -1- Let proj be identity{} for the overloads with no parameter named proj.