ranges::fold_meow
should explicitly spell out the return typeSection: 26.4 [algorithm.syn], 26.6.18 [alg.fold] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2024-05-03 Last modified: 2024-06-24
Priority: Not Prioritized
View all other issues in [algorithm.syn].
View all issues with Tentatively NAD status.
Discussion:
Unlike other algorithms, the return types of ranges::fold_meow
are specified in terms of
auto
and see below
, and its implementation details depend on the return types of
other overloads through decltype(fold_meow(...))
.
fold_right_last
)
extremely difficult even for experts, requiring several trips back and forth to different overloads
to finally understand what the actual return type is. The situation is even worse for newbies because
such a form of specifying the return type makes it impossible for the IDE to deduce the real return type,
which is extremely user-unfriendly.
I think that explicitly specifying the return type for these overloads not only greatly improves
readability but also offloads the compiler from deducing the return type, which can definitely be
considered an improvement.
The proposed resolution does not touch the Effects clause and only changes the function signature
to seek minimal changes.
[2024-06-24; Reflector poll: NAD]
Implementations are free to spell this out if desired.
Proposed resolution:
This wording is relative to N4981.
Modify 26.4 [algorithm.syn], header <algorithm>
synopsis, as indicated:
#include <initializer_list> // see 17.10.2 [initializer.list.syn] namespace std { […] namespace ranges { […] template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexpr auto fold_left(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexpr auto fold_left(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, T, range_reference_t<R>>>; template<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto fold_left_first(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto fold_left_first(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>; template<bidirectional_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-right-foldable<T, I> F> constexpr auto fold_right(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>; template<bidirectional_range R, class T = range_value_t<R>, indirectly-binary-right-foldable<T, iterator_t<R>> F> constexpr auto fold_right(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, range_reference_t<R>, T>>; template<bidirectional_iterator I, sentinel_for<I> S, indirectly-binary-right-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto fold_right_last(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_reference_t<I>, iter_value_t<I>>>>; template<bidirectional_range R, indirectly-binary-right-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto fold_right_last(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_reference_t<R>, range_value_t<R>>>>; template<class I, class T> using fold_left_with_iter_result = in_value_result<I, T>; template<class I, class T> using fold_left_first_with_iter_result = in_value_result<I, T>; template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexprsee belowauto fold_left_with_iter(I first, S last, T init, F f) -> fold_left_with_iter_result<I, decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexprsee belowauto fold_left_with_iter(R&& r, T init, F f) -> fold_left_with_iter_result<borrowed_iterator_t<R>, decay_t<invoke_result_t<F&, T, range_reference_t<R>>>>; template<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexprsee belowauto fold_left_first_with_iter(I first, S last, F f) -> fold_left_first_with_iter_result< I, optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexprsee belowauto fold_left_first_with_iter(R&& r, F f) -> fold_left_first_with_iter_result< borrowed_iterator_t<R>, optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>>; } […] }
Modify 26.6.18 [alg.fold] as indicated:
template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexpr auto ranges::fold_left(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexpr auto ranges::fold_left(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, T, range_reference_t<R>>>;-1- Returns:
ranges::fold_left_with_iter(std::move(first), last, std::move(init), f).valuetemplate<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto ranges::fold_left_first(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto ranges::fold_left_first(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>;-2- Returns:
ranges::fold_left_first_with_iter(std::move(first), last, f).valuetemplate<bidirectional_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-right-foldable<T, I> F> constexpr auto ranges::fold_right(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>; template<bidirectional_range R, class T = range_value_t<R>, indirectly-binary-right-foldable<T, iterator_t<R>> F> constexpr auto ranges::fold_right(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, range_reference_t<R>, T>>;-3- Effects: Equivalent to:
using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>; if (first == last) return U(std::move(init)); I tail = ranges::next(first, last); U accum = invoke(f, *--tail, std::move(init)); while (first != tail) accum = invoke(f, *--tail, std::move(accum)); return accum;template<bidirectional_iterator I, sentinel_for<I> S, indirectly-binary-right-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto ranges::fold_right_last(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_reference_t<I>, iter_value_t<I>>>>; template<bidirectional_range R, indirectly-binary-right-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto ranges::fold_right_last(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_reference_t<R>, range_value_t<R>>>>;-4- Let
-5- Effects: Equivalent to:U
bedecltype(ranges::fold_right(first, last, iter_value_t<I>(*first), f))
.if (first == last) return optional<U>(); I tail = ranges::prev(ranges::next(first, std::move(last))); return optional<U>(in_place, ranges::fold_right(std::move(first), tail, iter_value_t<I>(*tail), std::move(f)));template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexprsee belowauto ranges::fold_left_with_iter(I first, S last, T init, F f) -> fold_left_with_iter_result<I, decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexprsee belowauto ranges::fold_left_with_iter(R&& r, T init, F f) -> fold_left_with_iter_result<borrowed_iterator_t<R>, decay_t<invoke_result_t<F&, T, range_reference_t<R>>>>;-6- Let
-7- Effects: Equivalent to:U
bedecay_t<invoke_result_t<F&, T, iter_reference_t<I>>>
.if (first == last) return {std::move(first), U(std::move(init))}; U accum = invoke(f, std::move(init), *first); for (++first; first != last; ++first) accum = invoke(f, std::move(accum), *first); return {std::move(first), std::move(accum)};
-8- Remarks: The return type isfold_left_with_iter_result<I, U>
for the first overload andfold_left_with_iter_result<borrowed_iterator_t<R>, U>
for the second overload.template<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexprsee belowauto ranges::fold_left_first_with_iter(I first, S last, F f) -> fold_left_first_with_iter_result< I, optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexprsee belowauto ranges::fold_left_first_with_iter(R&& r, F f) -> fold_left_first_with_iter_result< borrowed_iterator_t<R>, optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>>;-9- Let
U
bedecltype(ranges::fold_left(std::move(first), last, iter_value_t<I>(*first), f))-10- Effects: Equivalent to:
if (first == last) return {std::move(first), optional<U>()}; optional<U> init(in_place, *first); for (++first; first != last; ++first) *init = invoke(f, std::move(*init), *first); return {std::move(first), std::move(init)};
-11- Remarks: The return type isfold_left_first_with_iter_result<I, optional<U>>
for the first overload andfold_left_first_with_iter_result<borrowed_iterator_t<R>, optional<U>>
for the second overload.