Date: Thu, 2 Nov 2023 11:58:50 -0400
Ville, I think you've temporarily forgotten the mess that is
`optional<bool>`.
"Empty" (as in "it's a range of zero elements, i.e. its value is the empty
sequence") and "disengaged" (as in "it has no value") are two different
concepts; and both are fundamentally different from "falsey" (as in "its
value is boolean false").
Implicit conversions are the root of much evil in C++.
On Thu, Nov 2, 2023 at 11:10 AM Ville Voutilainen via Std-Proposals <
std-proposals_at_[hidden]> wrote:
>
> A call to an overload set that deals with ranges or singular values. Thus:
>
> template<class T, class F> void checked_process_it(T&& t)
> {
> if (t)
> process_it(std::forward<T>(t));
> else
> nag("hey, don't do that");
> }
>
I think what the programmer meant here was more like:
if constexpr (!std::ranges::range<T>) {
process_it(std::views::single(std::forward<T>(t)));
} else {
process_it(t);
}
Obviously if `process_it` expects a ranges::range, it has to be prepared to
deal with the possibility that the range is empty (even if the way it deals
with that is just to say `if (ranges::empty(rg)) nag()`), since ranges *can*
be empty.
checked_process_it(std::vector<int>{}); // original code nags
checked_process_it(std::make_optional(std::vector<int>{})); //
original code fails to nag
Furthermore, if `checked_process_it` really intends to deal with optionals,
(smart) pointers, etc., then it is probably missing some code to unwrap the
optional/pointer after checking it for engagement/non-nullness. That is,
instead of
if (t)
process_it(t);
the programmer probably meant
if (t.has_value())
process_it(t.value()); // not just `t`!
and/or
if (t != nullptr)
process_it(*t); // not just `t`!
I can put it into a refined range concept that has the ability to
> check emptiness, though.
> And this really sounds like a bug in ranges that could be entertained
> to be fixed retroactively,
> even if that causes a little breakage. It's quite odd if a generic
> view that can be as lazy as imaginable
> can provide an empty() but a generic range can't.
>
FYI, some input ranges can't provide .empty().
The C++20 Ranges library solution is to provide a* free function* (well,
CPO) `ranges::empty` and tell people to use it instead. Ranges provides all
these "generic" primitive functions through CPOs instead of member
functions; that's just its style, for better and worse.
–Arthur
`optional<bool>`.
"Empty" (as in "it's a range of zero elements, i.e. its value is the empty
sequence") and "disengaged" (as in "it has no value") are two different
concepts; and both are fundamentally different from "falsey" (as in "its
value is boolean false").
Implicit conversions are the root of much evil in C++.
On Thu, Nov 2, 2023 at 11:10 AM Ville Voutilainen via Std-Proposals <
std-proposals_at_[hidden]> wrote:
>
> A call to an overload set that deals with ranges or singular values. Thus:
>
> template<class T, class F> void checked_process_it(T&& t)
> {
> if (t)
> process_it(std::forward<T>(t));
> else
> nag("hey, don't do that");
> }
>
I think what the programmer meant here was more like:
if constexpr (!std::ranges::range<T>) {
process_it(std::views::single(std::forward<T>(t)));
} else {
process_it(t);
}
Obviously if `process_it` expects a ranges::range, it has to be prepared to
deal with the possibility that the range is empty (even if the way it deals
with that is just to say `if (ranges::empty(rg)) nag()`), since ranges *can*
be empty.
checked_process_it(std::vector<int>{}); // original code nags
checked_process_it(std::make_optional(std::vector<int>{})); //
original code fails to nag
Furthermore, if `checked_process_it` really intends to deal with optionals,
(smart) pointers, etc., then it is probably missing some code to unwrap the
optional/pointer after checking it for engagement/non-nullness. That is,
instead of
if (t)
process_it(t);
the programmer probably meant
if (t.has_value())
process_it(t.value()); // not just `t`!
and/or
if (t != nullptr)
process_it(*t); // not just `t`!
I can put it into a refined range concept that has the ability to
> check emptiness, though.
> And this really sounds like a bug in ranges that could be entertained
> to be fixed retroactively,
> even if that causes a little breakage. It's quite odd if a generic
> view that can be as lazy as imaginable
> can provide an empty() but a generic range can't.
>
FYI, some input ranges can't provide .empty().
The C++20 Ranges library solution is to provide a* free function* (well,
CPO) `ranges::empty` and tell people to use it instead. Ranges provides all
these "generic" primitive functions through CPOs instead of member
functions; that's just its style, for better and worse.
–Arthur
Received on 2023-11-02 15:59:05