In a comment by @MarcvanLeeuwen to another Q&A, it was suggested that the following is undefined behavior (UB):
template<class FwdIt, class T>
FwdIt find_before(FwdIt before_first, FwdIt last, T const& value)
{
return std::adjacent_find(before_first, last,
[&](auto const& /* L */, auto const& R) {
// note that the left argument of the lambda is not evaluated
return R == value;
});
}
auto list = std::forward_list<int> { 1, 2 };
auto it = find_before(list.before_begin(), list.end(), 1); // undefined behavior!?
The UB comes from the fact that the BinaryPredicate
that is supplied to std::adjacent_find
will dereference two adjacent iterators at a time, the first pair being list.before_begin()
and list.begin()
. Since before_begin()
is not dereferencable, this could entail UB. On the other hand, the left argument is never used inside the lambda. One might argue that under the "as-if" rule it is unobservable whether the derefence actually took place, so that an optimizing compiler might elide it altogether.
Question: what does the Standard say about dereferencing an undereferencable iterator it
, when the expression *it
is not actually used and could be optimized away? (or more generally, is it UB when the offending expression is not used?)
NOTE: the offending code is easy to remedy by special casing the first iterator (as is done in the updated original Q&A).