c++ - Undefined behavior when the offending expression is not used? -


in comment @marcvanleeuwen another q&a, suggested following 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 left argument of lambda not evaluated         return r == value;      }); }  auto list = std::forward_list<int> { 1, 2 }; auto = find_before(list.before_begin(), list.end(), 1); // undefined behavior!? 

the ub comes fact binarypredicate supplied std::adjacent_find dereference 2 adjacent iterators @ time, first pair being list.before_begin() , list.begin(). since before_begin() not dereferencable, entail ub. on other hand, left argument never used inside lambda. 1 might argue under "as-if" rule unobservable whether derefence took place, optimizing compiler might elide altogether.

question: standard dereferencing undereferencable iterator it, when expression *it not used , optimized away? (or more generally, ub when offending expression not used?)

note: offending code easy remedy special casing first iterator (as done in updated original q&a).

before_begin() yields non-dereferenceable iterator; 1 applying unary * has undefined behavior, above code has undefined behavior:

1.3.24 [defns.undefined]

undefined behavior
behavior international standard imposes no requirements

now, true implementations of library, possible above code have defined behavior; example, in simple implementation of forward_list, operator* on iterator have effect of forming t& reference uninitialised area of aligned storage:

template<typename t> struct forward_list {     struct node {         node* next = nullptr;         typename aligned_storage<sizeof(t), alignof(t)>::type buf;     };     struct iterator {         node* node;         t& operator*() { return *static_cast<t*>(static_cast<void*>(node->buf)); }     };     node head;     iterator before_begin() { return {&head}; } }; 

in scenario *(list.before_begin()) has defined behavior, long result used in "limited ways" allowed 3.8p6.

however, small change scenario result in *(list.before_begin()) becoming undefined; example, optimising space implementation use node_base omits storage , derive concrete list nodes. or perhaps aligned storage contain object wrapping t, accessing wrapped t falls foul of 3.8p6. alternatively, debug implementation check dereferencing before-begin iterator , terminate program.

by as-if rule, implementation permitted eliminate evaluations have no visible side effects, doesn't result in code having defined behavior. if implementation debug-checks dereference, program termination side effect not eliminable.

more interestingly, if operation undefined behavior occurs on way forming t& reference, optimising compiler use infer code paths leading undefined behavior cannot occur. per reference implementation of adjacent_find, eliminating code paths leading offending *first give result find_before always return last.


Comments

Popular posts from this blog

javascript - RequestAnimationFrame not working when exiting fullscreen switching space on Safari -

Python ctypes access violation with const pointer arguments -