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

### 3779. `ranges::fold_*` can unintentionally `const_cast` and `reinterpret_cast`

Section: 27.6.18 [alg.fold] Status: Tentatively NAD Submitter: Nicole Mazzuca Opened: 2022-09-15 Last modified: 2022-10-12

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

In the Effects element of `ranges::fold_right`, we get the following code:

>     using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>;
>     if (first == last)
>       return U(std::move(init)); // functional-style C cast
>     […]

Given the following function object:

>     struct Second {
>       static char* operator()(const char*, char* rhs) {
>         return rhs;
>       }
>     };

calling `fold_right` as:

>     char* p = fold_right(views::empty<char*>, "Hello", Second{});

initializes `p` with `const_cast<char*>("Hello")`.

The same problem exists in `fold_left_with_iter`, and thus in `fold_left`.

One can get the `reinterpret_cast` behavior by replacing `const char*` with `unsigned long long`.

[2022-10-12; Reflector poll]

Set status to "Tentatively NAD" after reflector poll.

"The example doesn't compile. The accumulator should be be the second param, but with that fixed the constraints are not satisfied. The `convertible_to` constraint prevents the undesirable casting."

Proposed resolution:

This wording is relative to N4917.

1. Modify 27.6.18 [alg.fold] as indicated:

   >     template<bidirectional_iterator I, sentinel_for<I> S, class T,
   >              indirectly-binary-right-foldable<T, I> F>
   >       constexpr auto ranges::fold_right(I first, S last, T init, F f);
   >     template<bidirectional_range R, class T,
   >              indirectly-binary-right-foldable<T, iterator_t<R>> F>
   >       constexpr auto ranges::fold_right(R&& r, T init, F f);

   > > -3- Effects: Equivalent to:

   > > >     using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>;
   > > >     if (first == last)
   > > >       return static_cast<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<input_iterator I, sentinel_for<I> S, class T,
   >              indirectly-binary-left-foldable<T, I> F>
   >       constexpr see below ranges::fold_left_with_iter(I first, S last, T init, F f);
   >     template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
   >       constexpr see below ranges::fold_left_with_iter(R&& r, T init, F f);

   > > -6- Let `U` be `decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>`.

   > > -7- Effects: Equivalent to:

   > > >     if (first == last)
   > > >       return {std::move(first), static_cast<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)};