%!TEX root = std.tex \infannex{ub}{Core undefined behavior} \rSec1[ub.general]{General} \pnum This Annex documents undefined behavior explicitly called out in \ref{intro} through \ref{\lastcorechapter} using the following phrases: \begin{itemize} \item is undefined \item the behavior is undefined \item the behavior of the program is undefined \item has undefined behavior \item have undefined behavior \item result has undefined behavior \item results in undefined behavior \end{itemize} 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. \rSec1[ub.basic]{\ref*{basic}: Basics} \ubdescription{intro.object.implicit.create} \pnum 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\iref{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. \pnum \begin{example} \begin{codeblock} void f() { void *p = malloc(sizeof(int) + sizeof(float)); *reinterpret_cast(p) = 0; *reinterpret_cast(p) = 0.0f; // undefined behavior, cannot create // both \tcode{int} and \tcode{float} in same place } \end{codeblock} \end{example} \ubdescription{intro.object.implicit.pointer} \pnum After implicitly creating objects within a specified region of storage, some operations are described as producing a pointer to a suitable created object\iref{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. \pnum \begin{example} \begin{codeblock} #include struct X { int a, b; ~X() = delete; // deleted destructor makes \tcode{X} a non-implicit-lifetime class }; X* make_x() { X* p = (X*)std::malloc(sizeof(struct X)); // Cannot implicitly create an object because \tcode{X} // is not an implicit-lifetime class. p->a = 1; // undefined behavior, no set of objects give us defined behavior return p; } \end{codeblock} \end{example} \begin{example} \begin{codeblock} #include 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, \tcode{p2} points to an in-lifetime object of type \tcode{X}, // which is not similar to \tcode{Y}\iref{expr.ref} return p2; } \end{codeblock} \end{example} \ubdescription{basic.align.object.alignment} \pnum All instances of a type must be created in storage that meets the alignment requirement of that type. \pnum \begin{example} \begin{codeblock} struct alignas(4) S {}; void make_misaligned() { alignas(S) char s[sizeof(S) + 1]; new (&s+1) S(); // undefined behavior, \tcode{\&s+1} will yield a pointer to // a \tcode{char} which is $1$ byte away from an address with // an alignment of $4$ and so cannot have an alignment of $4$ } \end{codeblock} \end{example} \ubdescription{lifetime.outside.pointer.delete} \pnum 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 \grammarterm{delete-expression}. \pnum \begin{example} \begin{codeblock} struct S { float f = 0; ~S() {} }; void f() { S* p = new S; p->~S(); delete p; // undefined behavior, operand of \tcode{delete}, lifetime has ended and \tcode{S} // has a non-trivial destructor } \end{codeblock} \end{example} \ubdescription{lifetime.outside.pointer.member} \pnum 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. \pnum \begin{example} \begin{codeblock} 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{codeblock} \end{example} \ubdescription{lifetime.outside.pointer.virtual} \pnum For a pointer pointing to an object outside of its lifetime, behavior is undefined if the pointer is implicitly converted\iref{conv.ptr} to a pointer to a virtual base class (or base class of a virtual base class). \pnum \begin{example} \begin{codeblock} struct B {}; struct D : virtual B {}; void f() { D d; D* p = &d; d.~D(); B* b = p; // undefined behavior } \end{codeblock} \end{example} \ubdescription{lifetime.outside.pointer.dynamic.cast} \pnum For a pointer pointing to an object outside of its lifetime, behavior is undefined if the pointer is used as the operand of a \tcode{dynamic_cast}\iref{expr.dynamic.cast}. \pnum \begin{example} \begin{codeblock} struct B { virtual ~B() = default; }; struct D : B {}; void f() { D d; B* bp = &d; d.~D(); D* dp = dynamic_cast(bp); // undefined behavior } \end{codeblock} \end{example} \ubdescription{lifetime.outside.glvalue.access} \pnum Behavior is undefined if a glvalue referring to an object outside of its lifetime is used to access the object. \pnum \begin{example} \begin{codeblock} 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{codeblock} \end{example} \ubdescription{lifetime.outside.glvalue.member} \pnum 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. \pnum \begin{example} \begin{codeblock} 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{codeblock} \end{example} \ubdescription{lifetime.outside.glvalue.virtual} \pnum Behavior is undefined if a glvalue referring to an object outside of its lifetime is bound to a reference to a virtual base class. \pnum \begin{example} \begin{codeblock} struct B {}; struct D : virtual B { }; void f() { D d; d.~D(); B& b = d; // undefined behavior } \end{codeblock} \end{example} \ubdescription{lifetime.outside.glvalue.dynamic.cast} \pnum Behavior is undefined if a glvalue referring to an object outside of its lifetime is used as the operand of a \keyword{dynamic_cast} or as the operand of \keyword{typeid}. \pnum \begin{example} \begin{codeblock} struct B { virtual ~B(); }; struct D : virtual B {}; void f() { D d; B& br = d; d.~D(); D& dr = dynamic_cast(br); // undefined behavior } \end{codeblock} \end{example} \ubdescription{original.type.implicit.destructor} \pnum 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. \pnum \begin{example} \begin{codeblock} class T {}; struct B { ~B(); }; void h() { B b; new (&b) T; } // undefined behavior at block exit \end{codeblock} \end{example} \ubdescription{creating.within.const.complete.obj} \pnum 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. \pnum \begin{example} \begin{codeblock} struct B { B(); ~B(); }; const B b; void h() { b.~B(); new (const_cast(&b)) const B; // undefined behavior } \end{codeblock} \end{example} \ubdescription{basic.indet.value} \pnum When the result of an evaluation is an indeterminate value (but not just an erroneous value) the behavior is undefined. \pnum \begin{example} \begin{codeblock} void g() { int x [[ indeterminate ]]; int y = x; // undefined behavior } \end{codeblock} \end{example} \ubdescription{basic.stc.alloc.dealloc.constraint} \pnum If the behavior of an allocation or deallocation function does not satisfy the semantic constraints specified in~\ref{basic.stc.dynamic.allocation} and~\ref{basic.stc.dynamic.deallocation}, the behavior is undefined. \pnum \begin{example} \begin{codeblock} #include 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{codeblock} \end{example} \ubdescription{basic.stc.alloc.zero.dereference} \pnum The pointer returned when invoking an allocation function with a size of zero cannot be dereferenced. \pnum \begin{example} \begin{codeblock} void test() { char* c = static_cast(operator new(0z)); c[0] = 'X'; // undefined behavior } \end{codeblock} \end{example} \ubdescription{basic.compound.invalid.pointer} \pnum 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.) \pnum \begin{example} \begin{codeblock} void f() { int *x = new int{5}; delete x; int y = *x; // undefined behavior delete x; // undefined behavior } \end{codeblock} \end{example} \ubdescription{intro.execution.unsequenced.modification} \pnum If a side effect on a memory location\iref{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\iref{intro.multithread}, the behavior is undefined. \pnum \begin{example} \begin{codeblock} void g(int i) { i = 7, i++, i++; // \tcode{i} becomes 9 i = i++ + 1; // the value of \tcode{i} is incremented i = i++ + i; // undefined behavior i = i + 1; // the value of \tcode{i} is incremented } \end{codeblock} \end{example} \ubdescription{intro.races.data} \pnum 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~\ref{intro.races}. Any such data race results in undefined behavior. \pnum \begin{example} \begin{codeblock} int count = 0; auto f = [&] { count++; }; std::thread t1{f}, t2{f}, t3{f}; // undefined behavior \tcode{t1}, \tcode{t2} and \tcode{t3} have a data race on access of variable \tcode{count} \end{codeblock} \end{example} \ubdescription{intro.progress.stops} \pnum The behavior is undefined if a thread of execution that has not terminated stops making execution steps. \pnum \begin{example} \begin{codeblock} bool stop() { return false; } void busy_wait_thread() { while (!stop()); // undefined behavior, thread makes no progress but the loop } // is not trivial because \tcode{stop()} is not a constant expression int main() { std::thread t(busy_wait_thread); t.join(); } \end{codeblock} \end{example} \ubdescription{basic.start.main.exit.during.destruction} \pnum If \tcode{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. \pnum \begin{example} \begin{codeblock} #include struct Exiter { ~Exiter() { std::exit(0); } }; Exiter ex; int main() {} // undefined behavior when destructor of static variable \tcode{ex} is called it will call \tcode{std::exit} \end{codeblock} \end{example} \ubdescription{basic.start.term.use.after.destruction} \pnum 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. \pnum \begin{example} \begin{codeblock} struct A {}; void f() { static A a; } struct B { B() { f(); } }; struct C { ~C() { f(); } }; C c; B b; // call to \tcode{f()} in constructor begins lifetime of \tcode{a} int main() {} // undefined behavior, static objects are destructed in reverse order, in this case \tcode{a} then \tcode{b} and // finally \tcode{c}. When the destructor of \tcode{c} is called, it calls \tcode{f()} which passes through the definition of // previously destroyed block-scope object \end{codeblock} \end{example} \rSec1[ub.expr]{\ref*{expr}: Expressions} \ubdescription{expr.expr.eval} \pnum 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. \pnum \begin{example} \begin{codeblock} #include int main() { // Assuming $32$-bit \tcode{int}, the range of values is: $-2,147,483,648$ to $2,147,483,647$. int x1 = std::numeric_limits::max() + 1; // undefined behavior, $2,147,483,647 + 1$ is not representable as an int int x2 = std::numeric_limits::min() / -1; // undefined behavior, $-2,147,483,648 / -1$ is not representable as an int } \end{codeblock} \end{example} \ubdescription{expr.basic.lvalue.strict.aliasing.violation} \pnum If a program attempts to access\iref{defns.access} the stored value of an object whose dynamic type is $T$ through a glvalue whose type is not similar\iref{conv.qual} to $T$ (or its corresponding signed or unsigned types) the behavior is undefined. \pnum \begin{example} \begin{codeblock} int foo(float* f, int* i) { *i = 1; *f = 0.f; // undefined behavior, glvalue is not similar to \tcode{int} return *i; } int main() { int x = 0; x = foo(reinterpret_cast(&x), &x); } \end{codeblock} \end{example} \ubdescription{expr.basic.lvalue.union.initialization} \pnum 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. \pnum \begin{example} \begin{codeblock} union U { int x; }; void f() { char u[sizeof(U)]; U o = reinterpret_cast(u); // undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.type.reference.lifetime} \pnum Evaluating a reference when an equivalent use of a pointer denoting the same object would be invalid has undefined behavior. \pnum \begin{example} \begin{codeblock} void g() { int* ip = new int(5); int& i = *ip; delete ip; i; // undefined behavior } \end{codeblock} \end{example} \ubdescription{conv.lval.valid.representation} \pnum Performing an lvalue-to-rvalue conversion on an object whose value representation is not valid for its type has undefined behavior. \pnum \begin{example} \begin{codeblock} bool f() { bool b = true; char c = 42; memcpy(&b, &c, 1); return b; // undefined behavior if \tcode{42} is not a valid value representation for \keyword{bool} } \end{codeblock} \end{example} \ubdescription{conv.double.out.of.range} \pnum Converting a floating-point value to a type that cannot represent the value has undefined behavior. \pnum \begin{example} \begin{codeblock} #include int main() { // Assuming 32-bit int, 32-bit float and 64-bit double. double d2 = std::numeric_limits::max(); float f = d2; // undefined behavior on systems where the range of representable values // of \tcode{float} is \tcode{[-max,+max]}; on systems where the range of representable // values is \tcode{[-inf,+inf]} this would not be undefined behavior int i = d2; // undefined behavior, the max value of \tcode{double} is not representable as \tcode{int} } \end{codeblock} \end{example} \ubdescription{conv.fpint.float.not.represented} \pnum When converting a floating-point value to an integer type, if the value is not representable in the destination type, the behavior is undefined. \pnum \begin{example} \begin{codeblock} #include int main() { // Assuming $32$-bit \tcode{int}, the range of values is: $-2,147,483,648$ to // $2,147,483,647$. Assuming $32$-bit \tcode{float} and $64$-bit \tcode{double}. double d = (double)std::numeric_limits::max() + 1; int x1 = d; // undefined behavior, $2,147,483,647 + 1$ is not representable as \tcode{int} } \end{codeblock} \end{example} \ubdescription{conv.fpint.int.not.represented} \pnum 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. \pnum \begin{example} \begin{codeblock} int main() { unsigned long long x2 = -1; float f = x2; // undefined behavior on systems where \tcode{f} does not include // a representation for infinity and the maximum value for \tcode{float} // is smaller than the maximum value for \tcode{unsigned long long} } \end{codeblock} \end{example} \ubdescription{conv.ptr.virtual.base} \pnum Converting a pointer to a derived class \tcode{D} to a pointer to a virtual base class \tcode{B} that does not point to a valid object within its lifetime has undefined behavior. \pnum \begin{example} \begin{codeblock} struct B {}; struct D : virtual B {}; void f() { D ds[1]; B* b = &ds[1]; // undefined behavior } \end{codeblock} \end{example} \ubdescription{conv.member.missing.member} \pnum 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. \pnum \begin{example} \begin{codeblock} struct B {}; struct D1 : B { int d1; }; struct D2 : B {}; void f() { int (D1::*pd1) = &D1::d1; int (B::*pb) = static_cast(pd1); int (D2::*pd2) = pb; // undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.call.different.type} \pnum 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. \pnum \begin{example} \begin{codeblock} 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(f1)(20); // undefined behavior, the function type of the expression // is different from the called function's definition } \end{codeblock} \end{example} \ubdescription{expr.ref.member.not.similar} \pnum In a class member access \tcode{E1.E2}, where \tcode{E2} is a non-static member, the behavior is undefined if \tcode{E1} refers to an object whose type is not similar to the type of the expression \tcode{E1}. \pnum \begin{example} \begin{codeblock} struct A { int i; }; struct B { int j; }; A a; int x = reinterpret_cast(a).j; // undefined behavior \end{codeblock} \end{example} \ubdescription{expr.dynamic.cast.pointer.lifetime} \pnum Evaluating a \keyword{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. \pnum \begin{example} \begin{codeblock} struct B { virtual ~B(); }; void f() { B bs[1]; B* dp = dynamic_cast(bs+1); // undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.dynamic.cast.glvalue.lifetime} \pnum Evaluating a \keyword{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. \pnum \begin{example} \begin{codeblock} struct B { virtual ~B(); }; void f() { B bs[1]; B& dr = dynamic_cast(bs[1]); // undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.static.cast.base.class} \pnum A glvalue of type \tcode{B} can be cast to the type ``reference to \tcode{D}'' if \tcode{B} is a base class of \tcode{D}, otherwise the behavior is undefined. \pnum \begin{example} \begin{codeblock} struct B {}; struct D1 : B {}; struct D2 : B {}; void f() { D1 d; B &b = d; static_cast(b); // undefined behavior, base class object of type \tcode{D1} not \tcode{D2} } \end{codeblock} \end{example} \ubdescription{expr.static.cast.enum.outside.range} \pnum 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\iref{dcl.enum}, and otherwise, the behavior is undefined. \pnum \begin{example} \begin{codeblock} enum A { e1 = 1, e2 }; void f() { enum A a = static_cast(4); // undefined behavior, $4$ is not within the range of enumeration values } \end{codeblock} \end{example} \ubdescription{expr.static.cast.fp.outside.range} \pnum An explicit conversion of a floating-point value that is outside the range of the target type has undefined behavior. \pnum \begin{example} If \tcode{float} does not adhere to \IsoFloatUndated{} and cannot represent positive infinity, a sufficiently large \tcode{double} value will be outside the (finite) range of \tcode{float}. \begin{codeblock} void f() { double d = FLT_MAX; d *= 16; float f = static_cast(d); // undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.static.cast.downcast.wrong.derived.type} \pnum 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. \pnum \begin{example} \begin{codeblock} struct B {}; struct D1 : B {}; struct D2 : B {}; void f() { B *bp = new D1; static_cast(bp); // undefined behavior, base class object of type \tcode{D1} not \tcode{D2} } \end{codeblock} \end{example} \ubdescription{expr.static.cast.does.not.contain.original.member} \pnum A pointer to member of derived class \tcode{D} can be cast to a pointer to member of base class \tcode{B} (with certain restrictions on \cv{} qualifiers) as long as \tcode{B} contains the original member, or is a base or derived class of the class containing the original member; otherwise the behavior is undefined. \pnum \begin{example} \begin{codeblock} struct A { char c; }; struct B { int i; }; struct D : A, B {}; int main() { char D::*p1 = &D::c; char B::*p2 = static_cast(p1); // undefined behavior, \tcode{B} does not contain the // original member \tcode{c} } \end{codeblock} \end{example} \ubdescription{expr.unary.dereference} \pnum Dereferencing a pointer that does not point to an object or function has undefined behavior. \pnum \begin{example} \begin{codeblock} int f() { int *p = nullptr; return *p; // undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.new.non.allocating.null} \pnum If the allocation function is a non-allocating form\iref{new.delete.placement} that returns null, the behavior is undefined. \pnum \begin{example} \begin{codeblock} #include [[nodiscard]] void* operator new(std::size_t size, void* ptr) noexcept { return nullptr; // undefined behavior, should return non-null pointer } \end{codeblock} \end{example} \begin{example} \begin{codeblock} #include struct A { int x; }; int main() { char *p = nullptr; A *a = new (p) A; // undefined behavior, non-allocating new returning nullptr } \end{codeblock} \end{example} \ubdescription{expr.delete.mismatch} \pnum Using array delete on the result of a single object new expression has undefined behavior. \pnum \begin{example} \begin{codeblock} int* x = new int; delete[] x; // undefined behavior, allocated using single object new expression \end{codeblock} \end{example} \ubdescription{expr.delete.array.mismatch} \pnum Using single object delete on the result of an array new expression has undefined behavior. \pnum \begin{example} \begin{codeblock} int* x = new int[10]; delete x; // undefined behavior, allocated using array new expression \end{codeblock} \end{example} \ubdescription{expr.delete.dynamic.type.differ} \pnum 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. \pnum \begin{example} \begin{codeblock} struct B { int a; }; struct D : public B { int b; }; void f() { B* b = new D; delete b; // undefined behavior, no virtual destructor } \end{codeblock} \end{example} \ubdescription{expr.delete.dynamic.array.dynamic.type.differ} \pnum In an array delete expression, if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined. \pnum \begin{example} \begin{codeblock} 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 \tcode{D::operator delete[](void*, std::size_t)} B* bp = new D[i]; delete[] bp; // undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.mptr.oper.not.contain.member} \pnum Abbreviating \grammarterm{pm-expression}.*\grammarterm{cast-expression} as \tcode{E1.*E2}, \tcode{E1} is called the object expression. If the dynamic type of \tcode{E1} does not contain the member to which \tcode{E2} refers, the behavior is undefined. \pnum \begin{example} \begin{codeblock} struct B {}; struct D : B { int x; }; void f() { B *b = new B; D *d = static_cast(b); int D::*p = &D::x; (*d).*p = 1; // undefined behavior, dynamic type \tcode{B} does not contain \tcode{x} } \end{codeblock} \end{example} \ubdescription{expr.mptr.oper.member.func.null} \pnum If the second operand in a \tcode{.*} expression is the null member pointer value\iref{conv.mem}, the behavior is undefined. \pnum \begin{example} \begin{codeblock} struct S { int f(); }; void f() { S cs; int (S::*pm)() = nullptr; (cs.*pm)(); // undefined behavior, the second operand is null } \end{codeblock} \end{example} \ubdescription{expr.mul.div.by.zero} \pnum Division by zero has undefined behavior. \pnum \begin{example} \begin{codeblock} 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 \tcode{double} is \tcode{[-max,+max]}, on systems where // representable values is \tcode{[-inf,+inf]} this would not be undefined behavior } \end{codeblock} \end{example} \ubdescription{expr.mul.representable.type.result} \pnum If the quotient \tcode{a/b} is representable in the type of the result, \tcode{(a/b)*b + a\%b} is equal to \tcode{a}; otherwise, the behavior of both \tcode{a/b} and \tcode{a\%b} is undefined. \pnum \begin{example} \begin{codeblock} #include int main() { int x = std::numeric_limits::min() / -1; // Assuming \tcode{LP64} $-2,147,483,648$ which when divided by $-1$ // gives us $2,147,483,648$ which is not representable by \tcode{int}. } \end{codeblock} \end{example} \ubdescription{expr.add.out.of.bounds} \pnum Creating an out of bounds pointer has undefined behavior. \pnum \begin{example} \begin{codeblock} static const int arrs[10]{}; int main() { const int *y = arrs + 11; // undefined behavior, creating an out of bounds pointer } \end{codeblock} \end{example} \begin{example} \begin{codeblock} static const int arrs[10][10]{}; int main() { const int(*y)[10] = arrs + 11; // undefined behavior, creating an out of bounds pointer } \end{codeblock} \end{example} \ubdescription{expr.add.sub.diff.pointers} \pnum Subtracting pointers that are not part of the same array has undefined behavior. \pnum \begin{example} \begin{codeblock} #include void f() { int x[2]; int y[2]; int* p1 = x; int* p2 = y; std::ptrdiff_t off = p1 - p2; // undefined behavior, \tcode{p1} and \tcode{p2} point to different arrays } \end{codeblock} \end{example} \ubdescription{expr.add.not.similar} \pnum For addition or subtraction of two expressions $P$ and $Q$, if $P$ or $Q$ have type ``pointer to \cv{}~\tcode{T}'', where \tcode{T} and the array element type are not similar\iref{conv.rval}, the behavior is undefined. \pnum \begin{example} \begin{codeblock} 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, \tcode{T} is not similar to \tcode{S} but \tcode{s} points to a \tcode{T[]} @\commentellip@ } } int main() { T test[5]; f(test, 5); } \end{codeblock} \end{example} \ubdescription{expr.shift.neg.and.width} \pnum Shifting by a negative amount or equal or greater than the bit-width of a type has undefined behavior. \pnum \begin{example} \begin{codeblock} 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 \tcode{int} int y2 = 1 >> 32; // undefined behavior, shift is equal to the bit width of \tcode{int} \end{codeblock} \end{example} \ubdescription{expr.assign.overlap} \pnum Overlap in the storage between the source and destination can result in undefined behavior. \pnum \begin{example} \begin{codeblock} int x = 1; char* c = reinterpret_cast(&x); x = *c; // undefined behavior, source overlaps storage of destination \end{codeblock} \end{example} \rSec1[ub.stmt]{\ref*{stmt}: Statements} \ubdescription{stmt.return.flow.off} \pnum Flowing off the end of a function other than \tcode{main} or a coroutine results in undefined behavior if the return type is not \cv{}~\keyword{void}. \pnum \begin{example} \begin{codeblock} int f(int x) { if (x) return 1; // undefined behavior if \tcode{x} is \tcode{0} } void b() { int x = f(0); // undefined behavior, using \tcode{0} as an argument will cause \tcode{f(...)} to flow // off the end without a \tcode{return} statement } \end{codeblock} \end{example} \ubdescription{stmt.return.coroutine.flow.off} \pnum Flowing off the end of a coroutine function body that does not return \tcode{void} has undefined behavior. \pnum \begin{example} \begin{codeblock} #include #include #include #include class resumable { public: struct promise_type; using coro_handle = std::coroutine_handle; 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; 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 \tcode{void} } int main() { resumable res = foo(); while (res.resume()) ; std::cout << res.return_val() << std::endl; } \end{codeblock} \end{example} \ubdescription{stmt.dcl.local.static.init.recursive} \pnum If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined. \pnum \begin{example} \begin{codeblock} int foo(int i) { static int s = foo(2 * i); // undefined behavior, recursive call return i + 1; } \end{codeblock} \end{example} \rSec1[ub.dcl]{\ref*{dcl}: Declarations} \ubdescription{dcl.type.cv.modify.const.obj} \pnum Any attempt to modify a const object during its lifetime results in undefined behavior. \pnum \begin{example} \begin{codeblock} const int* ciq = new const int(3); // initialized as required int* iq = const_cast(ciq); // cast required *iq = 4; // undefined behavior, modifies a const object \end{codeblock} \end{example} \ubdescription{dcl.type.cv.access.volatile} \pnum 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. \pnum \begin{example} \begin{codeblock} volatile int x = 0; int& y = const_cast(x); std::cout << y; // undefined behavior, accessing volatile through non-volatile glvalue \end{codeblock} \end{example} \ubdescription{dcl.ref.incompatible.function} \pnum Initializing a reference to a function with a value that is a function that is not call-compatible\iref{expr.call} with the type of the reference has undefined behavior. \pnum \begin{example} \begin{codeblock} void f(float x); void (&g)(int) = reinterpret_cast(f); // undefined behavior \end{codeblock} \end{example} \ubdescription{dcl.ref.incompatible.type} \pnum Initializing a reference to an object with a value that is not type-accessible\iref{basic.lval} through the type of the reference has undefined behavior. \pnum \begin{example} \begin{codeblock} float g; int& i = reinterpret_cast(g); // undefined behavior \end{codeblock} \end{example} \ubdescription{dcl.ref.uninitialized.reference} \pnum Evaluating a reference prior to initializing that reference has undefined behavior. \pnum \begin{example} \begin{codeblock} extern int &ir1; int i2 = ir1; // undefined behavior, \tcode{ir1} not yet initialized int i3 = 17; int &ir1 = i3; \end{codeblock} \end{example} \ubdescription{dcl.fct.def.coroutine.resume.not.suspended} \pnum Invoking a resumption member function for a coroutine that is not suspended results in undefined behavior. \pnum \begin{example} \begin{codeblock} #include 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; 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 \tcode{coro.resume()} but \tcode{final_suspend} // has already returned, even though it returned \tcode{suspend_always} return sum; } \end{codeblock} \end{example} \ubdescription{dcl.fct.def.coroutine.destroy.not.suspended} \pnum Invoking \tcode{destroy()} on a coroutine that is not suspended has undefined behavior. \pnum \begin{example} \begin{codeblock} #include 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; 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{codeblock} \end{example} \ubdescription{dcl.attr.assume.false} \pnum If an assumption expression would not evaluate to true at the point where it appears the behavior is undefined. \pnum \begin{example} \begin{codeblock} int g(int x) { [[assume(x >= 0)]]; return x/32; } int f() { return g(-10); // undefined behavior, assumption in \tcode{g} is \tcode{false} } \end{codeblock} \end{example} \ubdescription{dcl.attr.noreturn.eventually.returns} \pnum If a function \tcode{f} is called where \tcode{f} was previously declared with the \tcode{noreturn} attribute and \tcode{f} eventually returns, the behavior is undefined. \pnum \begin{example} \begin{codeblock} [[noreturn]] void f(int i) { if (i > 0) throw "positive"; } int main() { f(0); // undefined behavior, returns from a \tcode{[[noreturn]]} function } \end{codeblock} \end{example} \rSec1[ub.class]{\ref*{class}: Classes} \ubdescription{class.dtor.no.longer.exists} \pnum 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. \pnum \begin{example} \begin{codeblock} struct A { ~A() {} }; int main() { A a; a.~A(); } // undefined behavior, lifetime of \tcode{a} already ended before implicit destructor \end{codeblock} \end{example} \ubdescription{class.abstract.pure.virtual} \pnum Calling a pure virtual function from a constructor or destructor in an abstract class has undefined behavior. \pnum \begin{example} \begin{codeblock} struct B { virtual void f() = 0; B() { f(); // undefined behavior, \tcode{f} is pure virtual and is being called from the constructor } }; struct D : B { void f() override; }; \end{codeblock} \end{example} \ubdescription{class.base.init.mem.fun} \pnum Calling a member function before all the \grammarterm{mem-initializer}s for base classes have completed has undefined behavior. \pnum \begin{example} \begin{codeblock} class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined behavior, calls member function but base \tcode{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 \tcode{C} not yet initialized i(f()) {} // defined, bases are all initialized }; \end{codeblock} \end{example} \ubdescription{class.cdtor.before.ctor} \pnum 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. \pnum \begin{example} \begin{codeblock} struct W { int j; }; struct X : public virtual W {}; struct Y { int *p; X x; Y() : p(&x.j) { // undefined behavior, \tcode{x} is not yet constructed } }; \end{codeblock} \end{example} \ubdescription{class.cdtor.after.dtor} \pnum 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. \pnum \begin{example} \begin{codeblock} 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{codeblock} \end{example} \ubdescription{class.cdtor.convert.pointer} \pnum 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. \pnum \begin{example} \begin{codeblock} 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 \tcode{E*} to \tcode{A*} might use path \tcode{E*} $\rightarrow$ \tcode{D*} $\rightarrow$ \tcode{A*} // but \tcode{D} is not constructed // ``\tcode{D((C*)this)}\!'' would be defined, \tcode{E*} $\rightarrow$ \tcode{C*} is defined because \tcode{E()} has started, // and \tcode{C*} $\rightarrow$ \tcode{A*} is defined because \tcode{C} is fully constructed X(this) {} // defined, upon construction of \tcode{X}, \tcode{C/B/D/A} sublattice is fully constructed }; \end{codeblock} \end{example} \ubdescription{class.cdtor.form.pointer} \pnum 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. \pnum \begin{example} \begin{codeblock} struct A { int i = 0; }; struct B { int *p; A a; B() : p(&a.i) {} // undefined behavior }; \end{codeblock} \end{example} \ubdescription{class.cdtor.virtual.not.x} \pnum 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 \tcode{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\iref{expr.ref} and the object expression refers to the complete object of \tcode{x} or one of that object's base class subobjects but not \tcode{x} or one of its base class subobjects, the behavior is undefined. \pnum \begin{example} \begin{codeblock} 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 \tcode{V::f}, not \tcode{A::f} g(); // calls \tcode{B::g}, not \tcode{D::g} v->g(); // \tcode{v} is base of \tcode{B}, the call is defined, calls \tcode{B::g} a->f(); // undefined behavior, \tcode{a}'s type not a base of \tcode{B} } \end{codeblock} \end{example} \ubdescription{class.cdtor.typeid} \pnum If the operand of \tcode{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. \pnum \begin{example} \begin{codeblock} 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); // \tcode{std::type_info} for \tcode{B} typeid(*v); // defined, \tcode{*v} has type \tcode{V}, a base of \tcode{B} yields \tcode{std::type_info} for \tcode{B} typeid(*a); // undefined behavior, type \tcode{A} not a base of \tcode{B} } \end{codeblock} \end{example} \ubdescription{class.cdtor.dynamic.cast} \pnum If the operand of the \tcode{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 \tcode{dynamic_cast} results in undefined behavior. \pnum \begin{example} \begin{codeblock} 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(v); // defined, \tcode{v} of type \tcode{V*}, \tcode{V} base of \tcode{B} results in \tcode{B*} dynamic_cast(a); // undefined behavior, \tcode{a} has type \tcode{A*}, \tcode{A} not a base of \tcode{B} } \end{codeblock} \end{example} \rSec1[ub.temp]{\ref*{temp}: Templates} \ubdescription{temp.inst.inf.recursion} \pnum The result of an infinite recursion in template instantiation is undefined. \pnum \begin{example} \begin{codeblock} template class X { X *p; // OK X a; // implicit instantiation of \tcode{X} requires // the implicit instantiation of \tcode{X} which requires // the implicit instantiation of \tcode{X} which \ldots }; int main() { X x; // undefined behavior, instantiation will lead to infinite recursion } \end{codeblock} \end{example} \rSec1[ub.except]{\ref*{except}: Exception handling} \ubdescription{except.handle.handler.ctor.dtor} \pnum Referring to any non-static member or base class of an object in the handler for a \grammarterm{function-try-block} of a constructor or destructor for that object results in undefined behavior. \pnum \begin{example} \begin{codeblock} #include struct A { A() try : x(0 ? 1 : throw 1) { } catch (int) { std::cout << "y: " << y << std::endl; // undefined behavior, referring to non-static member \tcode{y} in // the handler of function-try-block } int x; int y = 42; }; \end{codeblock} \end{example}