https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125981

--- Comment #7 from Ян Чуркин <yanchurkin at gmail dot com> ---
Right - after P2167R3 the requirement in [algorithms.requirements] is that
decltype(pred(*first)) "shall model boolean-testable".  The point is that the
predicate result type in this PR does model boolean-testable.  Here is a
self-contained check (compiles cleanly, -std=c++20 or c++23):

      #include <concepts>
      #include <cstdint>
      #include <utility>

      // exposition-only concept from [concept.booleantestable]
      template<class T> concept boolean_testable_impl
        = std::convertible_to<T, bool>;
      template<class T> concept boolean_testable =
        boolean_testable_impl<T>
        && requires (T&& t) { { !std::forward<T>(t) } -> boolean_testable_impl;
};

      // the predicate result type from the PR
      std::uint8_t value = 0;
      struct logic_t {
        logic_t operator!() { return logic_t(); }
        operator bool()     { return value; }
      };

      static_assert(boolean_testable<logic_t>);   // passes

logic_t is convertible to bool and !logic_t is too, so it models the concept.

The "&& and || resolve to the built-in operators" wording you quote is the
[concept.booleantestable] note, which is non-normative; neither the concept
nor P2167R3 adds anything that forbids a namespace-scope operator&&(bool, T).
So by the actual requirement the predicate is conforming input, and find_if
dereferencing the past-the-end iterator on an empty range is a defect.

It is also already established practice in the library to guard against this:
std::equal, std::binary_search and std::__partition already cast the
predicate/comparator result to bool for exactly this reason (e.g.
'if (!bool(__binary_pred(*__first1, *__first2)))').  This patch just makes
__find_if / __mismatch / __push_heap consistent with that.  No behavioural
change for well-behaved predicates; the ranges:: versions are unaffected
since their wrappers already return bool.

Reply via email to