Annex F (informative) Core undefined behavior [ub]

F.2 [basic]: Basics [ub.basic]

F.2.1[ub:intro.object.implicit.create]
Specified in: [intro.object]

For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types ([basic.types]) in its specified region of storage if doing so would result in the program having defined behavior.
If no such set of objects would give the program defined behavior, the behavior of the program is undefined.
[Example 1: void f() { void *p = malloc(sizeof(int) + sizeof(float)); *reinterpret_cast<int*>(p) = 0; *reinterpret_cast<float*>(p) = 0.0f; // undefined behavior, cannot create // both int and float in same place } — end example]

F.2.2[ub:intro.object.implicit.pointer]
Specified in: [intro.object]

After implicitly creating objects within a specified region of storage, some operations are described as producing a pointer to a suitable created object ([basic.types]).
These operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object, if that value would result in the program having defined behavior.
If no such pointer value would give the program defined behavior, the behavior of the program is undefined.
[Example 1: #include <cstdlib> struct X { int a, b; ~X() = delete; // deleted destructor makes X a non-implicit-lifetime class }; X* make_x() { X* p = (X*)std::malloc(sizeof(struct X)); // Cannot implicitly create an object because X // is not an implicit-lifetime class. p->a = 1; // undefined behavior, no set of objects give us defined behavior return p; } — end example]
[Example 2: #include <cstdlib> struct X { int a; }; struct Y { int x; }; Y* make_y() { X* p1 = (X*)std::malloc(sizeof(struct X)); // object #1 p1->a = 1; Y* p2 = (Y*)p1; p2->x = 2; // undefined behavior, p2 points to an in-lifetime object of type X, // which is not similar to Y ([expr.ref]) return p2; } — end example]

F.2.3[ub:basic.align.object.alignment]
Specified in: [basic.align]

All instances of a type must be created in storage that meets the alignment requirement of that type.
[Example 1: struct alignas(4) S {}; void make_misaligned() { alignas(S) char s[sizeof(S) + 1]; new (&s+1) S(); // undefined behavior, &s+1 will yield a pointer to // a char which is 1 byte away from an address with // an alignment of 4 and so cannot have an alignment of 4 } — end example]

F.2.4[ub:lifetime.outside.pointer.delete]
Specified in: [basic.life]

For a pointer pointing to an object outside of its lifetime, behavior is undefined if the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression.
[Example 1: struct S { float f = 0; ~S() {} }; void f() { S* p = new S; p->~S(); delete p; // undefined behavior, operand of delete, lifetime has ended and S // has a non-trivial destructor } — end example]

F.2.5[ub:lifetime.outside.pointer.member]
Specified in: [basic.life]

For a pointer pointing to an object outside of its lifetime, behavior is undefined if the pointer is used to access a non-static data member or call a non-static member function of the object.
[Example 1: struct S { float f = 0; }; float f() { S s; S* p = &s; s.~S(); return p->f; // undefined behavior, accessing non-static data member after end of lifetime } — end example]

F.2.6[ub:lifetime.outside.pointer.virtual]
Specified in: [basic.life]

For a pointer pointing to an object outside of its lifetime, behavior is undefined if the pointer is implicitly converted ([conv.ptr]) to a pointer to a virtual base class (or base class of a virtual base class).
[Example 1: struct B {}; struct D : virtual B {}; void f() { D d; D* p = &d; d.~D(); B* b = p; // undefined behavior } — end example]

F.2.7[ub:lifetime.outside.pointer.dynamic.cast]
Specified in: [basic.life]

For a pointer pointing to an object outside of its lifetime, behavior is undefined if the pointer is used as the operand of a dynamic_cast ([expr.dynamic.cast]).
[Example 1: struct B { virtual ~B() = default; }; struct D : B {}; void f() { D d; B* bp = &d; d.~D(); D* dp = dynamic_cast<D*>(bp); // undefined behavior } — end example]

F.2.8[ub:lifetime.outside.glvalue.access]
Specified in: [basic.life]

Behavior is undefined if a glvalue referring to an object outside of its lifetime is used to access the object.
[Example 1: void f() { int x = int{10}; using T = int; x.~T(); int y = x; // undefined behavior, glvalue used to access the // object after the lifetime has ended } — end example]

F.2.9[ub:lifetime.outside.glvalue.member]
Specified in: [basic.life]

Behavior is undefined if a glvalue referring to an object outside of its lifetime is used to call a non-static member function of the object.
[Example 1: struct A { void f() {} }; void f() { A a; a.~A(); a.f(); // undefined behavior, glvalue used to access a non-static member // function after the lifetime has ended } — end example]

F.2.10[ub:lifetime.outside.glvalue.virtual]
Specified in: [basic.life]

Behavior is undefined if a glvalue referring to an object outside of its lifetime is bound to a reference to a virtual base class.
[Example 1: struct B {}; struct D : virtual B { }; void f() { D d; d.~D(); B& b = d; // undefined behavior } — end example]

F.2.11[ub:lifetime.outside.glvalue.dynamic.cast]
Specified in: [basic.life]

Behavior is undefined if a glvalue referring to an object outside of its lifetime is used as the operand of a dynamic_cast or as the operand of typeid.
[Example 1: struct B { virtual ~B(); }; struct D : virtual B {}; void f() { D d; B& br = d; d.~D(); D& dr = dynamic_cast<D&>(br); // undefined behavior } — end example]

F.2.12[ub:original.type.implicit.destructor]
Specified in: [basic.life]

The behavior is undefined if a non-trivial implicit destructor call occurs when the type of the object inhabiting the associated storage is not the original type associated with that storage.
[Example 1: class T {}; struct B { ~B(); }; void h() { B b; new (&b) T; } // undefined behavior at block exit — end example]

F.2.13[ub:creating.within.const.complete.obj]
Specified in: [basic.life]

Creating a new object within the storage that a const complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
[Example 1: struct B { B(); ~B(); }; const B b; void h() { b.~B(); new (const_cast<B*>(&b)) const B; // undefined behavior } — end example]

F.2.14[ub:basic.indet.value]
Specified in: [basic.indet]

When the result of an evaluation is an indeterminate value (but not just an erroneous value) the behavior is undefined.
[Example 1: void g() { int x [[ indeterminate ]]; int y = x; // undefined behavior } — end example]

F.2.15[ub:basic.stc.alloc.dealloc.constraint]

If the behavior of an allocation or deallocation function does not satisfy the semantic constraints specified in [basic.stc.dynamic.allocation] and [basic.stc.dynamic.deallocation], the behavior is undefined.
[Example 1: #include <new> void* operator new(std::size_t sz) { if (sz == 0) return nullptr; // undefined behavior, should return non-null pointer return std::malloc(1); // undefined behavior, if successful should return allocation with // length in bytes is at least as large as the requested size } void operator delete(void* ptr) noexcept { throw 0; // undefined behavior, terminates by throwing an exception } — end example]

F.2.16[ub:basic.stc.alloc.dealloc.throw]

If a call to a deallocation function terminates by throwing an exception the behavior is undefined.
[Example 1: struct X { void operator delete(void*) noexcept(false) { throw "oops"; } }; void f() { X* x = new X(); delete x; // undefined behavior } — end example]

F.2.17[ub:basic.stc.alloc.zero.dereference]

The pointer returned when invoking an allocation function with a size of zero cannot be dereferenced.
[Example 1: void test() { char* c = static_cast<char*>(operator new(0z)); c[0] = 'X'; // undefined behavior } — end example]

F.2.18[ub:basic.compound.invalid.pointer]
Specified in: [basic.compound]

Indirection or the invocation of a deallocation function with a pointer value referencing storage that has been freed has undefined behavior.
(Most other uses of such a pointer have implementation-defined behavior.)
[Example 1: void f() { int *x = new int{5}; delete x; int y = *x; // undefined behavior delete x; // undefined behavior } — end example]

F.2.19[ub:intro.execution.unsequenced.modification]
Specified in: [intro.execution]

If a side effect on a memory location ([intro.memory]) is unsequenced relative to either another side effect on the same memory location or a value computation using the value of any object in the same memory location, and they are not potentially concurrent ([intro.multithread]), the behavior is undefined.
[Example 1: void g(int i) { i = 7, i++, i++; // i becomes 9 i = i++ + 1; // the value of i is incremented i = i++ + i; // undefined behavior i = i + 1; // the value of i is incremented } — end example]

F.2.20[ub:intro.races.data]
Specified in: [intro.races]

The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, except for the special case for signal handlers described in [intro.races].
Any such data race results in undefined behavior.
[Example 1: int count = 0; auto f = [&] { count++; }; std::thread t1{f}, t2{f}, t3{f}; // undefined behavior t1, t2 and t3 have a data race on access of variable count — end example]

F.2.21[ub:intro.progress.stops]
Specified in: [intro.progress]

The behavior is undefined if a thread of execution that has not terminated stops making execution steps.
[Example 1: bool stop() { return false; } void busy_wait_thread() { while (!stop()); // undefined behavior, thread makes no progress but the loop } // is not trivial because stop() is not a constant expression int main() { std::thread t(busy_wait_thread); t.join(); } — end example]

F.2.22[ub:basic.start.main.exit.during.destruction]
Specified in: [basic.start.main]

If std​::​exit is called to end a program during the destruction of an object with static or thread storage duration, the program has undefined behavior.
[Example 1: #include <cstdlib> struct Exiter { ~Exiter() { std::exit(0); } }; Exiter ex; int main() {} // undefined behavior when destructor of static variable ex is called it will call std​::​exit — end example]

F.2.23[ub:basic.start.term.use.after.destruction]
Specified in: [basic.start.term]

If a function contains a block-scope object of static or thread storage duration that has been destroyed and the function is called during the destruction of an object with static or thread storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed block-scope object.
Likewise, the behavior is undefined if the block-scope object is used indirectly (i.e., through a pointer) after its destruction.
[Example 1: struct A {}; void f() { static A a; } struct B { B() { f(); } }; struct C { ~C() { f(); } }; C c; B b; // call to f() in constructor begins lifetime of a int main() {} // undefined behavior, static objects are destructed in reverse order, in this case a then b and // finally c. When the destructor of c is called, it calls f() which passes through the definition of // previously destroyed block-scope object — end example]