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]
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 behaviorreturn 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]
[Example 1: structalignas(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]
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]
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]
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).
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]
[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]
[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]
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]
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.
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: #include<new>void*operatornew(std::size_t sz){if(sz ==0)returnnullptr; // undefined behavior, should return non-null pointerreturn std::malloc(1); // undefined behavior, if successful should return allocation with// length in bytes is at least as large as the requested size}voidoperatordelete(void* ptr)noexcept{throw0; // undefined behavior, terminates by throwing an exception} — end example]
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]
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].
[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]
[Example 1: bool stop(){returnfalse; }void busy_wait_thread(){while(!stop()); // undefined behavior, thread makes no progress but the loop}// is not trivial because stop() is not a constant expressionint main(){
std::thread t(busy_wait_thread);
t.join();
} — end example]
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]
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 aint 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]