This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of LEWG status.
ranges::for_each(_n) should be less constrainedSection: 26.6.5 [alg.foreach] Status: LEWG Submitter: Jiang An Opened: 2025-04-08 Last modified: 2025-10-23
Priority: 3
View other active issues in [alg.foreach].
View all other issues in [alg.foreach].
View all issues with LEWG 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.
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.
[2025-10-23; Reflector poll; Status changed: New → LEWG and P3.]
This is design change.
More algorithms (like count_if, any_of, etc.) that never produce
iter_value_t are affected.
Example of affected code:
std::vector<std::string> v;
// the following not working, constraints not satisfied
std::ranges::for_each(
v | std::views::as_rvalue,
[](std::string&& s) { }
);
Proposed resolution:
This wording is relative to N5008.
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 = {});
}
[…]
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 namespacerangesrequireFunto modelcopy_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 namespacerangesrequiresFunto modelcopy_constructible. — end note]