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.

4241. ranges::for_each(_n) should be less constrained

Section: 26.6.5 [alg.foreach] Status: New Submitter: Jiang An Opened: 2025-04-08 Last modified: 2025-04-13

Priority: Not Prioritized

View other active issues in [alg.foreach].

View all other issues in [alg.foreach].

View all issues with New status.

Discussion:

Currently, ranges::for_each(_n) are constrained with indirectly_unary_invocable, which doesn't meet the actual use of the range elements. These algorithms are only expected to invoke the callable object with the possibly projected elements, and not to use any element as the value type. Moreover, indirectly_unary_invocable requires the callable object to be copy constructible, which might be undesired because the corresponding std::for_each(_n) only require move constructibilty.

LWG 4171(i) talked about the breakage around ranges::for_each introduced by P2609R3. P2609R3 looks like a reasonable fix as long as the affected algorithms potentially use the intermediate element values copied as std::iter_value_t<I>. However, when the algorithm is not expected to or even required not to do this, P2609R3 can bring unexpected impacts. It seems that constraints around iter_value_t should be avoided for such an algorithm.

Proposed resolution:

This wording is relative to N5008.

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

    […]
    namespace ranges {
      template<class I, class F>
      using for_each_result = in_fun_result<I, F>;
      
      template<input_iterator I, sentinel_for<I> S, class Proj = identity,
               indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
        requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
        constexpr for_each_result<I, Fun>
          for_each(I first, S last, Fun f, Proj proj = {});
      template<input_range R, class Proj = identity,
               indirectly_unary_invocable<projected<iterator_t<R>, Proj>>move_constructible Fun>
        requires invocable<Fun&, iter_reference_t<projected<iterator_t<R>, Proj>>>
        constexpr for_each_result<borrowed_iterator_t<R>, Fun>
          for_each(R&& r, Fun f, Proj proj = {});
    }
    […]
    namespace ranges {
      template<class I, class F>
      using for_each_n_result = in_fun_result<I, F>;
    
      template<input_iterator I, class Proj = identity,
               indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
         requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
        constexpr for_each_n_result<I, Fun>
          for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
    }
    […]
    
  2. Modify 26.6.5 [alg.foreach] as indicated:

    template<input_iterator I, sentinel_for<I> S, class Proj = identity,
             indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
      requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
      constexpr ranges::for_each_result<I, Fun>
        ranges::for_each(I first, S last, Fun f, Proj proj = {});
    template<input_range R, class Proj = identity,
             indirectly_unary_invocable<projected<iterator_t<R>, Proj>>move_constructible Fun>
      requires invocable<Fun&, iter_reference_t<projected<iterator_t<R>, Proj>>>
      constexpr ranges::for_each_result<borrowed_iterator_t<R>, Fun>
        ranges::for_each(R&& r, Fun f, Proj proj = {});
    

    […]

    -15- [Note 6: The overloads in namespace ranges require Fun to model copy_constructible. — end note]

    […]

    template<input_iterator I, class Proj = identity,
             indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
      requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
      constexpr ranges::for_each_n_result<I, Fun>
        ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
    

    […]

    -30- [Note 11: The overload in namespace ranges requires Fun to model copy_constructible. — end note]