You can create a temporary object as part of the evaluation of an expression.
For example:
int getOffset(int i) {
int x = std::vector{10, 20, 30}[i] / (i + 1);
return x +std::stoi("15");
}
The code above contains two full expressions (declaration of i
and return
), each contains a temporary:
-
std::vector{10, 20, 30}
is a temporary vector created in-place, and accessed directly.
-
"15"
is implicitly converted to temporary std::string{"15"}
because std::stoi
accepts std::string
const&
as an argument.
A temporary object remains valid throughout the single full expression where it was created. It is reclaimed and destroyed (for nontrivial objects)
at the end of it.
If after the end of a full expression, some references or pointers still refer to a temporary object they immediately become dangling, unless the
lifetime of the temporary object is extended (see Exceptions).
Dereferencing and even copying such dangling pointers or references causes undefined behavior.
Exceptions
A reference-to-const
directly bound to a temporary object extends the lifetime of this object. The object enjoys the same lifetime as
the reference it is bound to.
However, lifetime extension is not transitive. If the definition of the temporary relies on another temporary, this second temporary will still be
destroyed at the end of the full expression, creating an immediately dangling reference.
What is the potential impact?
Accessing a dangling reference or pointer causes undefined behavior. This means the compiler is not bound by the language standard anymore and your
program has no meaning assigned to it.
Practically this has a wide range of effects. In many cases, the access works by accident and succeeds at writing or reading a value. However, it
can start misbehaving at any time. If compilation flags, compiler, platform, or runtime environment change, the same code can crash the application,
corrupt memory, or leak a secret.