Annex F (informative) Core undefined behavior [ub]

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]