Can anyone please explain why the std::predicate
concept requires that the result of a predicate invoke be implicitly converted to bool
(i.e., it does not allow objects whose operator bool
has an explicit
specifier to be used as a predicate)?
Example:
class TA
{
public:
operator bool() const noexcept
{
return {};
}
};
class TB
{
public:
explicit operator bool() const noexcept
{
return {};
}
};
void test_predicate()
{
static_assert(std::predicate<std::identity, TA>); //Ok
static_assert(std::predicate<std::identity, TB>); //Failed
}
In fact, predicates are used only in selection and iteration statements, and such statements allow the use of explicit operator bool
.
Those std::predicate
restrictions seem redundant, and this has consequences in practice.
For example, to determine how many elements of std::vector<std::optional<int>> A
have a value, you can simply write:
std::count_if(A.begin(), A.end(), std::identity{});
But you can't write the same using constrained
algorithms:
std::ranges::count_if(A, std::identity{}); \\compile error
std::count_if(A.begin(), A.end(), std::identity{});
shouldn't1 be doing what you think it is doing. It should be counting the non-zero values, not the present int
s. Requiring implicit conversion to bool
helps avoid subtle bugs like this.
In fact, predicates are used only in selection and iteration statements
No, they are used in other places, that don't allow contextual conversions.
template<class InputIterator, class Predicate> constexpr typename iterator_traits<InputIterator>::difference_type count_if(InputIterator first, InputIterator last, Predicate pred);
Let E be
pred(*i) != false
.Effects: Returns the number of iterators
i
in the range [first
,last
) for which E holds.
alg.count
std::optional<T>
is only equality comparable to bool
if T
is.
template<class T, class U> constexpr bool operator!=(const optional<T>& x, const U& v);
Equivalent to:
return x.has_value() ? *x != v : true;
optional.comp.with.t
std::count_if
and std::ranges::count_if
have the same requirements on their respective type parameters, but std::ranges::count_if
additionally has a requires
clause which enforces them. I think std::count_if(A.begin(), A.end(), std::identity{});
is strictly ill-formed.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With