Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does std::predicate require *implicit* conversion to bool?

Tags:

c++

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

like image 755
lpv_pvl Avatar asked Sep 02 '25 14:09

lpv_pvl


1 Answers

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 ints. 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

  1. 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.
like image 173
Caleth Avatar answered Sep 05 '25 05:09

Caleth