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.