This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of NAD status.
ranges::fold_*
can unintentionally const_cast
and reinterpret_cast
Section: 26.6.18 [alg.fold] Status: NAD Submitter: Nicole Mazzuca Opened: 2022-09-15 Last modified: 2022-11-30
Priority: Not Prioritized
View other active issues in [alg.fold].
View all other issues in [alg.fold].
View all issues with 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")
.
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."
[2022-11-30 LWG telecon. Status changed: Tentatively NAD → NAD.]
Proposed resolution:
This wording is relative to N4917.
Modify 26.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
-7- Effects: Equivalent to:U
bedecay_t<invoke_result_t<F&, T, iter_reference_t<I>>>
.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)};