Annex F (informative) Core undefined behavior [ub]


F.1 General [ub.general]

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

F.3 [expr]: Expressions [ub.expr]

F.4 [stmt]: Statements [ub.stmt]

F.5 [dcl]: Declarations [ub.dcl]

F.6 [class]: Classes [ub.class]

F.7 [temp]: Templates [ub.temp]

F.8 [except]: Exception handling [ub.except]


F.1 General [ub.general]

This Annex documents undefined behavior explicitly called out in [intro] through [cpp] using the following phrases:
  • is undefined
  • the behavior is undefined
  • the behavior of the program is undefined
  • has undefined behavior
  • have undefined behavior
  • result has undefined behavior
  • results in undefined behavior
Undefined behavior that is implicit is not covered by this annex.
Each entry contains a title, a cross-reference, a summary of the circumstances, and code examples.
The code examples are not intended to exhaustively cover all possible ways of invoking that case.

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.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.17[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.18[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.19[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.20[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.21[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.22[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]

F.3 [expr]: Expressions [ub.expr]

F.3.1[ub:expr.expr.eval]
Specified in: [expr.pre]

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.
[Example 1: #include <limits> int main() { // Assuming 32-bit int, the range of values is: to 2,147,483,647. int x1 = std::numeric_limits<int>::max() + 1; // undefined behavior, is not representable as an int int x2 = std::numeric_limits<int>::min() / -1; // undefined behavior, is not representable as an int } — end example]

F.3.2[ub:expr.basic.lvalue.strict.aliasing.violation]
Specified in: [basic.lval]

If a program attempts to access ([defns.access]) the stored value of an object whose dynamic type is T through a glvalue whose type is not similar ([conv.qual]) to T (or its corresponding signed or unsigned types) the behavior is undefined.
[Example 1: int foo(float* f, int* i) { *i = 1; *f = 0.f; // undefined behavior, glvalue is not similar to int return *i; } int main() { int x = 0; x = foo(reinterpret_cast<float*>(&x), &x); } — end example]

F.3.3[ub:expr.basic.lvalue.union.initialization]
Specified in: [basic.lval]

If a program invokes a defaulted copy/move constructor or defaulted copy/move assignment operator of a union with an argument that is not an object of a similar type within its lifetime, the behavior is undefined.
[Example 1: union U { int x; }; void f() { char u[sizeof(U)]; U o = reinterpret_cast<U&>(u); // undefined behavior } — end example]

F.3.4[ub:expr.type.reference.lifetime]
Specified in: [expr.type]

Evaluating a reference when an equivalent use of a pointer denoting the same object would be invalid has undefined behavior.
[Example 1: void g() { int* ip = new int(5); int& i = *ip; delete ip; i; // undefined behavior } — end example]

F.3.5[ub:conv.lval.valid.representation]
Specified in: [conv.lval]

Performing an lvalue-to-rvalue conversion on an object whose value representation is not valid for its type has undefined behavior.
[Example 1: bool f() { bool b = true; char c = 42; memcpy(&b, &c, 1); return b; // undefined behavior if 42 is not a valid value representation for bool } — end example]

F.3.6[ub:conv.double.out.of.range]
Specified in: [conv.double]

Converting a floating-point value to a type that cannot represent the value has undefined behavior.
[Example 1: #include <limits> int main() { // Assuming 32-bit int, 32-bit float and 64-bit double. double d2 = std::numeric_limits<double>::max(); float f = d2; // undefined behavior on systems where the range of representable values // of float is [-max,+max]; on systems where the range of representable // values is [-inf,+inf] this would not be undefined behavior int i = d2; // undefined behavior, the max value of double is not representable as int } — end example]

F.3.7[ub:conv.fpint.float.not.represented]
Specified in: [conv.fpint]

When converting a floating-point value to an integer type, if the value is not representable in the destination type, the behavior is undefined.
[Example 1: #include <limits> int main() { // Assuming 32-bit int, the range of values is: to // 2,147,483,647. Assuming 32-bit float and 64-bit double. double d = (double)std::numeric_limits<int>::max() + 1; int x1 = d; // undefined behavior is not representable as int } — end example]

F.3.8[ub:conv.fpint.int.not.represented]
Specified in: [conv.fpint]

When converting a value of integer or unscoped enumeration type to a floating-point type, if the value is not representable in the destination type the behavior is undefined.
[Example 1: int main() { unsigned long long x2 = -1; float f = x2; // undefined behavior on systems where f does not include // a representation for infinity and the maximum value for float // is smaller than the maximum value for unsigned long long } — end example]

F.3.9[ub:conv.ptr.virtual.base]
Specified in: [conv.ptr]

Converting a pointer to a derived class D to a pointer to a virtual base class B that does not point to a valid object within its lifetime has undefined behavior.
[Example 1: struct B {}; struct D : virtual B {}; void f() { D ds[1]; B* b = &ds[1]; // undefined behavior } — end example]

F.3.10[ub:conv.member.missing.member]
Specified in: [conv.mem]

The conversion of a pointer to a member of a base class to a pointer to member of a derived class that could not contain that member has undefined behavior.
[Example 1: struct B {}; struct D1 : B { int d1; }; struct D2 : B {}; void f() { int (D1::*pd1) = &D1::d1; int (B::*pb) = static_cast<int (B::*)>(pd1); int (D2::*pd2) = pb; // undefined behavior } — end example]

F.3.11[ub:expr.call.different.type]
Specified in: [expr.call]

Calling a function through an expression whose function type is not call-compatible with the function type of the called function's definition results in undefined behavior.
[Example 1: using f_float = int (*)(float); using f_int = int (*)(int); int f1(float) { return 10; } int f2(int) { return 20; } int main() { return reinterpret_cast<f_int>(f1)(20); // undefined behavior, the function type of the expression // is different from the called functions definition } — end example]

F.3.12[ub:expr.ref.member.not.similar]
Specified in: [expr.ref]

In a class member access E1.E2, where E2 is a non-static member, the behavior is undefined if E1 refers to an object whose type is not similar to the type of the expression E1.
[Example 1: struct A { int i; }; struct B { int j; }; A a; int x = reinterpret_cast<B&>(a).j; // undefined behavior — end example]

F.3.13[ub:expr.dynamic.cast.pointer.lifetime]
Specified in: [expr.dynamic.cast]

Evaluating a dynamic_cast on a non-null pointer that points to an object (of polymorphic type) of the wrong type or to an object not within its lifetime has undefined behavior.
[Example 1: struct B { virtual ~B(); }; void f() { B bs[1]; B* dp = dynamic_cast<B*>(bs+1); // undefined behavior } — end example]

F.3.14[ub:expr.dynamic.cast.glvalue.lifetime]
Specified in: [expr.dynamic.cast]

Evaluating a dynamic_cast on a reference that denotes an object (of polymorphic type) of a type that is not similar to the referenced type or an object not within its lifetime has undefined behavior.
[Example 1: struct B { virtual ~B(); }; void f() { B bs[1]; B& dr = dynamic_cast<B&>(bs[1]); // undefined behavior } — end example]

F.3.15[ub:expr.static.cast.base.class]
Specified in: [expr.static.cast]

A glvalue of type B can be cast to the type “reference to D” if B is a base class of D, otherwise the behavior is undefined.
[Example 1: struct B {}; struct D1 : B {}; struct D2 : B {}; void f() { D1 d; B &b = d; static_cast<D2 &>(b); // undefined behavior, base class object of type D1 not D2 } — end example]

F.3.16[ub:expr.static.cast.enum.outside.range]
Specified in: [expr.static.cast]

If the enumeration type does not have a fixed underlying type, the value is unchanged if the original value is within the range of the enumeration values ([dcl.enum]), and otherwise, the behavior is undefined.
[Example 1: enum A { e1 = 1, e2 }; void f() { enum A a = static_cast<A>(4); // undefined behavior, 4 is not within the range of enumeration values } — end example]

F.3.17[ub:expr.static.cast.fp.outside.range]
Specified in: [expr.static.cast]

An explicit conversion of a floating-point value that is outside the range of the target type has undefined behavior.
[Example 1: 
If float does not adhere to ISO/IEC 60559 and cannot represent positive infinity, a sufficiently large double value will be outside the (finite) range of float.
void f() { double d = FLT_MAX; d *= 16; float f = static_cast<float>(d); // undefined behavior } — end example]

F.3.18[ub:expr.static.cast.downcast.wrong.derived.type]
Specified in: [expr.static.cast]

Casting from a pointer to a base class to a pointer to a derived class when there is no enclosing object of that derived class at the specified location has undefined behavior.
[Example 1: struct B {}; struct D1 : B {}; struct D2 : B {}; void f() { B *bp = new D1; static_cast<D2 *>(bp); // undefined behavior, base class object of type D1 not D2 } — end example]

F.3.19[ub:expr.static.cast.does.not.contain.original.member]
Specified in: [expr.static.cast]

A pointer to member of derived class D can be cast to a pointer to member of base class B (with certain restrictions on cv qualifiers) as long as B contains the original member, or is a base or derived class of the class containing the original member; otherwise the behavior is undefined.
[Example 1: struct A { char c; }; struct B { int i; }; struct D : A, B {}; int main() { char D::*p1 = &D::c; char B::*p2 = static_cast<char B::*>(p1); // undefined behavior, B does not contain the original member c } — end example]

F.3.20[ub:expr.unary.dereference]
Specified in: [expr.unary.op]

Dereferencing a pointer that does not point to an object or function has undefined behavior.
[Example 1: int f() { int *p = nullptr; return *p; // undefined behavior } — end example]

F.3.21[ub:expr.new.non.allocating.null]
Specified in: [expr.new]

If the allocation function is a non-allocating form ([new.delete.placement]) that returns null, the behavior is undefined.
[Example 1: #include <new> [[nodiscard]] void* operator new(std::size_t size, void* ptr) noexcept { return nullptr; // undefined behavior, should return non-null pointer } — end example]
[Example 2: #include <new> struct A { int x; }; int main() { char *p = nullptr; A *a = new (p) A; // undefined behavior, non-allocating new returning nullptr } — end example]

F.3.22[ub:expr.delete.mismatch]
Specified in: [expr.delete]

Using array delete on the result of a single object new expression has undefined behavior.
[Example 1: int* x = new int; delete[] x; // undefined behavior, allocated using single object new expression — end example]

F.3.23[ub:expr.delete.array.mismatch]
Specified in: [expr.delete]

Using single object delete on the result of an array new expression has undefined behavior.
[Example 1: int* x = new int[10]; delete x; // undefined behavior, allocated using array new expression — end example]

F.3.24[ub:expr.delete.dynamic.type.differ]
Specified in: [expr.delete]

If the static type of the object to be deleted is different from its dynamic type and the selected deallocation function is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
[Example 1: struct B { int a; }; struct D : public B { int b; }; void f() { B* b = new D; delete b; // undefined behavior, no virtual destructor } — end example]

F.3.25[ub:expr.delete.dynamic.array.dynamic.type.differ]
Specified in: [expr.delete]

In an array delete expression, if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
[Example 1: struct B { virtual ~B(); void operator delete[](void*, std::size_t); }; struct D : B { void operator delete[](void*, std::size_t); }; void f(int i) { D* dp = new D[i]; delete[] dp; // uses D​::​operator delete[](void*, std​::​size_t) B* bp = new D[i]; delete[] bp; // undefined behavior } — end example]

F.3.26[ub:expr.mptr.oper.not.contain.member]
Specified in: [expr.mptr.oper]

Abbreviating pm-expression.
*cast-expression as E1.*E2, E1 is called the object expression.
If the dynamic type of E1 does not contain the member to which E2 refers, the behavior is undefined.
[Example 1: struct B {}; struct D : B { int x; }; void f() { B *b = new B; D *d = static_cast<D *>(b); int D::*p = &D::x; (*d).*p = 1; // undefined behavior, dynamic type B does not contain x } — end example]

F.3.27[ub:expr.mptr.oper.member.func.null]
Specified in: [expr.mptr.oper]

If the second operand in a .* expression is the null member pointer value ([conv.mem]), the behavior is undefined.
[Example 1: struct S { int f(); }; void f() { S cs; int (S::*pm)() = nullptr; (cs.*pm)(); // undefined behavior, the second operand is null } — end example]

F.3.28[ub:expr.mul.div.by.zero]
Specified in: [expr.mul]

Division by zero has undefined behavior.
[Example 1: int main() { int x = 1 / 0; // undefined behavior, division by zero double d = 1.0 / 0.0; // undefined behavior on systems where the range of // representable values of double is [-max,+max], on systems where // representable values is [-inf,+inf] this would not be undefined behavior } — end example]

F.3.29[ub:expr.mul.representable.type.result]
Specified in: [expr.mul]

If the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a; otherwise, the behavior of both a/b and a%b is undefined.
[Example 1: #include <limits> int main() { int x = std::numeric_limits<int>::min() / -1; // Assuming LP64 which when divided by // gives us 2,147,483,648 which is not representable by int. } — end example]

F.3.30[ub:expr.add.out.of.bounds]
Specified in: [expr.add]

Creating an out of bounds pointer has undefined behavior.
[Example 1: static const int arrs[10]{}; int main() { const int *y = arrs + 11; // undefined behavior, creating an out of bounds pointer } — end example]
[Example 2: static const int arrs[10][10]{}; int main() { const int(*y)[10] = arrs + 11; // undefined behavior, creating an out of bounds pointer } — end example]

F.3.31[ub:expr.add.sub.diff.pointers]
Specified in: [expr.add]

Subtracting pointers that are not part of the same array has undefined behavior.
[Example 1: #include <cstddef> void f() { int x[2]; int y[2]; int* p1 = x; int* p2 = y; std::ptrdiff_t off = p1 - p2; // undefined behavior, p1 and p2 point to different arrays } — end example]

F.3.32[ub:expr.add.not.similar]
Specified in: [expr.add]

For addition or subtraction of two expressions P and Q, if P or Q have type “pointer to cv T”, where T and the array element type are not similar ([conv.rval]), the behavior is undefined.
[Example 1: struct S { int i; }; struct T : S { double d; }; void f(const S* s, std::size_t count) { for (const S* end = s + count; s != end; ++s) { // undefined behavior, T is not similar to S but s points to a T[] /* ... */ } } int main() { T test[5]; f(test, 5); } — end example]

F.3.33[ub:expr.shift.neg.and.width]
Specified in: [expr.shift]

Shifting by a negative amount or equal or greater than the bit-width of a type has undefined behavior.
[Example 1: int y = 1 << -1; // undefined behavior, shift is negative static_assert(sizeof(int) == 4 && CHAR_BIT == 8); int y1 = 1 << 32; // undefined behavior, shift is equal to the bit width of int int y2 = 1 >> 32; // undefined behavior, shift is equal to the bit width of int — end example]

F.3.34[ub:expr.assign.overlap]
Specified in: [expr.assign]

Overlap in the storage between the source and destination can result in undefined behavior.
[Example 1: int x = 1; char* c = reinterpret_cast<char*>(&x); x = *c; // undefined behavior, source overlaps storage of destination — end example]

F.4 [stmt]: Statements [ub.stmt]

F.4.1[ub:stmt.return.flow.off]
Specified in: [stmt.return]

Flowing off the end of a function other than main or a coroutine results in undefined behavior if the return type is not cv void.
[Example 1: int f(int x) { if (x) return 1; // undefined behavior if x is 0 } void b() { int x = f(0); // undefined behavior, using 0 as an argument will cause f(...) to flow // off the end without a return statement } — end example]

F.4.2[ub:stmt.return.coroutine.flow.off]

Flowing off the end of a coroutine function body that does not return void has undefined behavior.
[Example 1: #include <cassert> #include <coroutine> #include <iostream> #include <vector> class resumable { public: struct promise_type; using coro_handle = std::coroutine_handle<promise_type>; resumable(coro_handle handle) : handle_(handle) { assert(handle); } resumable(resumable&) = delete; resumable(resumable&&) = delete; bool resume() { if (not handle_.done()) handle_.resume(); return not handle_.done(); } ~resumable() { handle_.destroy(); } const char* return_val(); private: coro_handle handle_; }; struct resumable::promise_type { using coro_handle = std::coroutine_handle<promise_type>; const char* string_; auto get_return_object() { return coro_handle::from_promise(*this); } auto initial_suspend() { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() { std::terminate(); } void return_value(const char* string) { string_ = string; } }; const char* resumable::return_val() { return handle_.promise().string_; } resumable foo() { std::cout << "Hello" << std::endl; co_await std::suspend_always(); // undefined behavior, falling off the end of coroutine that does not return void } int main() { resumable res = foo(); while (res.resume()) ; std::cout << res.return_val() << std::endl; } — end example]

F.4.3[ub:stmt.dcl.local.static.init.recursive]
Specified in: [stmt.dcl]

If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.
[Example 1: int foo(int i) { static int s = foo(2 * i); // undefined behavior, recursive call return i + 1; } — end example]

F.5 [dcl]: Declarations [ub.dcl]

F.5.1[ub:dcl.type.cv.modify.const.obj]
Specified in: [dcl.type.cv]

Any attempt to modify a const object during its lifetime results in undefined behavior.
[Example 1: const int* ciq = new const int(3); // initialized as required int* iq = const_cast<int*>(ciq); // cast required *iq = 4; // undefined behavior, modifies a const object — end example]

F.5.2[ub:dcl.type.cv.access.volatile]
Specified in: [dcl.type.cv]

If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined
[Example 1: volatile int x = 0; int& y = const_cast<int&>(x); std::cout << y; // undefined behavior, accessing volatile through non-volatile glvalue — end example]

F.5.3[ub:dcl.ref.incompatible.function]
Specified in: [dcl.ref]

Initializing a reference to a function with a value that is a function that is not call-compatible ([expr.call]) with the type of the reference has undefined behavior.
[Example 1: void f(float x); void (&g)(int) = reinterpret_cast<void (&)(int)>(f); // undefined behavior — end example]

F.5.4[ub:dcl.ref.incompatible.type]
Specified in: [dcl.ref]

Initializing a reference to an object with a value that is not type-accessible ([basic.lval]) through the type of the reference has undefined behavior.
[Example 1: float g; int& i = reinterpret_cast<int&>(g); // undefined behavior — end example]

F.5.5[ub:dcl.ref.uninitialized.reference]
Specified in: [dcl.ref]

Evaluating a reference prior to initializing that reference has undefined behavior.
[Example 1: extern int &ir1; int i2 = ir1; // undefined behavior, ir1 not yet initialized int i3 = 17; int &ir1 = i3; — end example]

F.5.6[ub:dcl.fct.def.coroutine.resume.not.suspended]

Invoking a resumption member function for a coroutine that is not suspended results in undefined behavior.
[Example 1: #include <coroutine> struct minig { struct promise_type { int val; minig get_return_object() { return {*this}; } constexpr suspend_always initial_suspend() noexcept { return {}; } constexpr suspend_always final_suspend() noexcept { return {}; } constexpr void return_void() noexcept {} [[noreturn]] void unhandled_exception() noexcept { throw; } suspend_always yield_value(int v) noexcept { val = v; return {}; } }; using HDL = coroutine_handle<promise_type>; HDL coro; minig(promise_type& p) : coro(HDL::from_promise(p)) {} ~minig() { coro.destroy(); } bool move_next() { coro.resume(); return !coro.done(); } int current_value() { return coro.promise().val; } }; static minig f(int n) { for (int i = 0; i < n; ++i) co_yield i; } int main() { auto g = f(10); int sum = 0; while (g.move_next()) sum += g.current_value(); g.move_next(); // undefined behavior, will call coro.resume() but final_suspend // has already returned, even though it returned suspend_always return sum; } — end example]

F.5.7[ub:dcl.fct.def.coroutine.destroy.not.suspended]

Invoking destroy() on a coroutine that is not suspended has undefined behavior.
[Example 1: #include <coroutine> struct minig { struct promise_type { int val; minig get_return_object() { return {*this}; } constexpr suspend_always initial_suspend() noexcept { return {}; } constexpr suspend_always final_suspend() noexcept { return {}; } constexpr void return_void() noexcept {} [[noreturn]] void unhandled_exception() { throw; } suspend_always yield_value(int v) noexcept { val = v; return {}; } }; using HDL = coroutine_handle<promise_type>; HDL coro; minig(promise_type& p) : coro(HDL::from_promise(p)) {} ~minig() { coro.destroy(); } bool move_next() { coro.resume(); return !coro.done(); } int current_value() { // this coroutine is not suspended therefore a call to destroy has undefined behavior coro.destroy(); return coro.promise().val; } }; static minig f(int n) { for (int i = 0; i < n; ++i) co_yield i; } int main() { auto g = f(10); int sum = 0; while (g.move_next()) sum += g.current_value(); return sum; } — end example]

F.5.8[ub:dcl.attr.assume.false]
Specified in: [dcl.attr.assume]

If an assumption expression would not evaluate to true at the point where it appears the behavior is undefined.
[Example 1: int g(int x) { [[assume(x >= 0)]]; return x/32; } int f() { return g(-10); // undefined behavior, assumption in g is false } — end example]

F.5.9[ub:dcl.attr.noreturn.eventually.returns]
Specified in: [dcl.attr.noreturn]

If a function f is called where f was previously declared with the noreturn attribute and f eventually returns, the behavior is undefined.
[Example 1: [[noreturn]] void f(int i) { if (i > 0) throw "positive"; } int main() { f(0); // undefined behavior, returns from a [[noreturn]] function } — end example]

F.6 [class]: Classes [ub.class]

F.6.1[ub:class.dtor.no.longer.exists]
Specified in: [class.dtor]

Once a destructor is invoked for an object, the object's lifetime has ended; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended.
[Example 1: struct A { ~A() {} }; int main() { A a; a.~A(); } // undefined behavior, lifetime of a already ended before implicit destructor — end example]

F.6.2[ub:class.abstract.pure.virtual]
Specified in: [class.abstract]

Calling a pure virtual function from a constructor or destructor in an abstract class has undefined behavior.
[Example 1: struct B { virtual void f() = 0; B() { f(); // undefined behavior, f is pure virtual and is being called from the constructor } }; struct D : B { void f() override; }; — end example]

F.6.3[ub:class.base.init.mem.fun]
Specified in: [class.base.init]

Calling a member function before all the mem-initializers for base classes have completed has undefined behavior.
[Example 1: class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined behavior, calls member function but base A not yet initialized j(f()) {} // defined, bases are all initialized }; class C { public: C(int); }; class D : public B, C { int i; public: D() : C(f()), // undefined behavior, calls member function but base C not yet initialized i(f()) {} // defined, bases are all initialized }; — end example]

F.6.4[ub:class.cdtor.before.ctor]
Specified in: [class.cdtor]

For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.
[Example 1: struct W { int j; }; struct X : public virtual W {}; struct Y { int *p; X x; Y() : p(&x.j) { // undefined behavior, x is not yet constructed } }; — end example]

F.6.5[ub:class.cdtor.after.dtor]
Specified in: [class.cdtor]

For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution has undefined behavior.
[Example 1: struct X { int i; ~X(); // non-trivial }; X& g() { static X x; return x; } void f() { X* px = &g(); px->~X(); int j = px->i; // undefined behavior } — end example]

F.6.6[ub:class.cdtor.convert.pointer]
Specified in: [class.cdtor]

Converting a pointer to a base class of an object, when construction of that object has not started or destruction of that object has finished, has undefined behavior.
[Example 1: struct A { }; struct B : virtual A { }; struct C : B { }; struct D : virtual A { D(A*); }; struct X { X(A*); }; struct E : C, D, X { E() : D(this), // undefined behavior, upcast from E* to A* might use path E* D* A* // but D is not constructed // “D((C*)this)'' would be defined, E* C* is defined because E() has started, // and C* A* is defined because C is fully constructed X(this) {} // defined, upon construction of X, C/B/D/A sublattice is fully constructed }; — end example]

F.6.7[ub:class.cdtor.form.pointer]
Specified in: [class.cdtor]

When forming a pointer to a direct non-static member of a class, construction must have started and destruction must not have finished otherwise the behavior is undefined.
[Example 1: struct A { int i = 0; }; struct B { int *p; A a; B() : p(&a.i) {} // undefined behavior }; — end example]

F.6.8[ub:class.cdtor.virtual.not.x]
Specified in: [class.cdtor]

When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class.
If the virtual function call uses an explicit class member access ([expr.ref]) and the object expression refers to the complete object of x or one of that object's base class subobjects but not x or one of its base class subobjects, the behavior is undefined.
[Example 1: struct V { virtual void f(); virtual void g(); }; struct A : virtual V { virtual void f(); }; struct B : virtual V { virtual void g(); B(V *, A *); }; struct D : A, B { virtual void f(); virtual void g(); D() : B((A *)this, this) {} }; B::B(V *v, A *a) { f(); // calls V​::​f, not A​::​f g(); // calls B​::​g, not D​::​g v->g(); // v is base of B, the call is defined, calls B​::​g a->f(); // undefined behavior, a's type not a base of B } — end example]

F.6.9[ub:class.cdtor.typeid]
Specified in: [class.cdtor]

If the operand of typeid refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.
[Example 1: struct V { virtual void f(); }; struct A : virtual V {}; struct B : virtual V { B(V *, A *); }; struct D : A, B { D() : B((A *)this, this) {} }; B::B(V *v, A *a) { typeid(*this); // std​::​type_info for B typeid(*v); // defined, *v has type V, a base of B yields std​::​type_info for B typeid(*a); // undefined behavior, type A not a base of B } — end example]

F.6.10[ub:class.cdtor.dynamic.cast]
Specified in: [class.cdtor]

If the operand of the dynamic_cast refers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, the dynamic_cast results in undefined behavior.
[Example 1: struct V { virtual void f(); }; struct A : virtual V {}; struct B : virtual V { B(V *, A *); }; struct D : A, B { D() : B((A *)this, this) {} }; B::B(V *v, A *a) { dynamic_cast<B *>(v); // defined, v of type V*, V base of B results in B* dynamic_cast<B *>(a); // undefined behavior, a has type A*, A not a base of B } — end example]

F.7 [temp]: Templates [ub.temp]

F.7.1[ub:temp.inst.inf.recursion]
Specified in: [temp.inst]

The result of an infinite recursion in template instantiation is undefined.
[Example 1: template <class T> class X { X<T> *p; // OK X<T *> a; // implicit instantiation of X<T> requires // the implicit instantiation of X<T*> which requires // the implicit instantiation of X<T**> which … }; int main() { X<int> x; // undefined behavior, instantiation will lead to infinite recursion } — end example]

F.8 [except]: Exception handling [ub.except]

F.8.1[ub:except.handle.handler.ctor.dtor]
Specified in: [except.handle]

Referring to any non-static member or base class of an object in the handler for a function-try-block of a constructor or destructor for that object results in undefined behavior.
[Example 1: #include <iostream> struct A { A() try : x(0 ? 1 : throw 1) { } catch (int) { std::cout << "y: " << y << std::endl; // undefined behavior, referring to non-static member y in // the handler of function-try-block } int x; int y = 42; }; — end example]