Files
2025-10-25 03:02:53 +03:00

424 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[class.temporary]
# 6 Basics [[basic]](./#basic)
## 6.8 Memory and objects [[basic.memobj]](basic.memobj#class.temporary)
### 6.8.7 Temporary objects [class.temporary]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4614)
A [*temporary object*](#def:object,temporary "6.8.7Temporary objects[class.temporary]") is an object created
- [(1.1)](#1.1)
when a prvalue is converted to an xvalue ([[conv.rval]](conv.rval "7.3.5Temporary materialization conversion")) and
- [(1.2)](#1.2)
when needed by the implementation to pass or return an object of suitable type (see below)[.](#1.sentence-1)
Even when the creation of the temporary object is
unevaluated ([[expr.context]](expr.context "7.2.3Context dependence")),
all the semantic restrictions shall be respected as if the temporary object
had been created and later destroyed[.](#1.sentence-2)
[*Note [1](#note-1)*:
This includes accessibility ([[class.access]](class.access "11.8Member access control")) and whether it is deleted,
for the constructor selected and for the destructor[.](#1.sentence-3)
However, in the special
case of the operand of a[*decltype-specifier*](dcl.type.decltype#nt:decltype-specifier "9.2.9.6Decltype specifiers[dcl.type.decltype]") ([[dcl.type.decltype]](dcl.type.decltype "9.2.9.6Decltype specifiers")), no temporary is introduced,
so the foregoing does not apply to such a prvalue[.](#1.sentence-4)
— *end note*]
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4638)
The materialization of a temporary object is generally
delayed as long as possible
in order to avoid creating unnecessary temporary objects[.](#2.sentence-1)
[*Note [2](#note-2)*:
Temporary objects are materialized:
- [(2.1)](#2.1)
when binding a reference to a prvalue ([[dcl.init.ref]](dcl.init.ref "9.5.4References"), [[expr.type.conv]](expr.type.conv "7.6.1.4Explicit type conversion (functional notation)"), [[expr.dynamic.cast]](expr.dynamic.cast "7.6.1.7Dynamic cast"), [[expr.static.cast]](expr.static.cast "7.6.1.9Static cast"), [[expr.const.cast]](expr.const.cast "7.6.1.11Const cast"), [[expr.cast]](expr.cast "7.6.3Explicit type conversion (cast notation)")),
- [(2.2)](#2.2)
when performing certain member accesses on a class prvalue ([[expr.ref]](expr.ref "7.6.1.5Class member access"), [[expr.mptr.oper]](expr.mptr.oper "7.6.4Pointer-to-member operators")),
- [(2.3)](#2.3)
when invoking an implicit object member function on a class prvalue ([[expr.call]](expr.call "7.6.1.3Function call")),
- [(2.4)](#2.4)
when performing an array-to-pointer conversion or subscripting on an array prvalue ([[conv.array]](conv.array "7.3.3Array-to-pointer conversion"), [[expr.sub]](expr.sub "7.6.1.2Subscripting")),
- [(2.5)](#2.5)
when initializing an object of type std::initializer_list<T> from a [*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1General[dcl.init.general]") ([[dcl.init.list]](dcl.init.list "9.5.5List-initialization")),
- [(2.6)](#2.6)
for certain unevaluated operands ([[expr.typeid]](expr.typeid "7.6.1.8Type identification"), [[expr.sizeof]](expr.sizeof "7.6.2.5Sizeof")), and
- [(2.7)](#2.7)
when a prvalue that has type other than cv void appears as a discarded-value expression ([[expr.context]](expr.context "7.2.3Context dependence"))[.](#2.sentence-2)
— *end note*]
[*Example [1](#example-1)*:
Consider the following code:class X {public: X(int);
X(const X&);
X& operator=(const X&); ~X();};
class Y {public: Y(int);
Y(Y&&); ~Y();};
X f(X);
Y g(Y);
void h() { X a(1);
X b = f(X(2));
Y c = g(Y(3));
a = f(a);}
X(2) is constructed in the space used to hold f()'s argument andY(3) is constructed in the space used to hold g()'s argument[.](#2.sentence-4)
Likewise,f()'s result is constructed directly in b andg()'s result is constructed directly in c[.](#2.sentence-5)
On the other hand, the expressiona = f(a) requires a temporary for
the result of f(a),
which is materialized so that the reference parameter
of X::operator=(const X&) can bind to it[.](#2.sentence-6)
— *end example*]
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4706)
When an object of class type X is passed to or returned from a potentially-evaluated function call,
if X is
- [(3.1)](#3.1)
a scalar type or
- [(3.2)](#3.2)
a class type that
has at least one eligible copy or move constructor ([[special]](special "11.4.4Special member functions")),
where each such constructor is trivial,
and the destructor of X is either trivial or deleted,
implementations are permitted
to create temporary objects
to hold the function parameter or result object,
as follows:
- [(3.3)](#3.3)
The first such temporary object
is constructed from the function argument or return value, respectively[.](#3.3.sentence-1)
- [(3.4)](#3.4)
Each successive temporary object
is initialized from the previous one
as if by direct-initialization if X is a scalar type,
otherwise by using an eligible trivial constructor[.](#3.4.sentence-1)
- [(3.5)](#3.5)
The function parameter or return object is initialized
from the final temporary
as if by direct-initialization if X is a scalar type,
otherwise by using an eligible trivial constructor[.](#3.5.sentence-1)
(In all cases, the eligible constructor is used
even if that constructor is inaccessible
or would not be selected by overload resolution
to perform a copy or move of the object)[.](#3.sentence-2)
[*Note [3](#note-3)*:
This latitude is granted to allow objects
to be passed to or returned from functions in registers[.](#3.sentence-3)
— *end note*]
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4747)
Temporary objects are destroyed as the last step
in evaluating
the full-expression ([[intro.execution]](intro.execution "6.10.1Sequential execution"))
that (lexically) contains the point where
they were created[.](#4.sentence-1)
This is true even if that evaluation ends in throwing an exception[.](#4.sentence-2)
Thevalue computations andside effects of destroying a temporary object
are associated only with the full-expression, not with any specific
subexpression[.](#4.sentence-3)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4765)
There are several contexts in which temporaries are destroyed at a different
point than the end of the full-expression[.](#5.sentence-1)
The first context is when a default constructor is called to initialize
an element of an array with no corresponding initializer ([[dcl.init]](dcl.init "9.5Initializers"))[.](#5.sentence-2)
The second context is when a copy constructor is called to copy an element of
an array while the entire array is copied ([[expr.prim.lambda.capture]](expr.prim.lambda.capture "7.5.6.3Captures"), [[class.copy.ctor]](class.copy.ctor "11.4.5.3Copy/move constructors"))[.](#5.sentence-3)
In either case, if the constructor has one or more default arguments,
the destruction of every temporary created in a default argument is
sequenced before the construction of the next array element, if any[.](#5.sentence-4)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4778)
The third context is when a reference binds to a temporary object[.](#6.sentence-1)[25](#footnote-25 "The same rules apply to initialization of an initializer_­list object ([dcl.init.list]) with its underlying temporary array.")
The temporary object to which the reference is bound or the temporary object
that is the complete object of a subobject to which the reference is bound
persists for the lifetime of the reference if the glvalue
to which the reference is bound
was obtained through one of the following:
- [(6.1)](#6.1)
a temporary materialization conversion ([[conv.rval]](conv.rval "7.3.5Temporary materialization conversion")),
- [(6.2)](#6.2)
( [*expression*](expr.comma#nt:expression "7.6.20Comma operator[expr.comma]") ),
where [*expression*](expr.comma#nt:expression "7.6.20Comma operator[expr.comma]") is one of these expressions,
- [(6.3)](#6.3)
subscripting ([[expr.sub]](expr.sub "7.6.1.2Subscripting")) of an array operand,
where that operand is one of these expressions,
- [(6.4)](#6.4)
a class member access ([[expr.ref]](expr.ref "7.6.1.5Class member access")) using the . operator
where the left operand is one of these expressions and
the right operand designates a non-static data member of non-reference type,
- [(6.5)](#6.5)
a pointer-to-member operation ([[expr.mptr.oper]](expr.mptr.oper "7.6.4Pointer-to-member operators")) using the .* operator
where the left operand is one of these expressions and
the right operand is a pointer to data member of non-reference type,
- [(6.6)](#6.6)
a
* [(6.6.1)](#6.6.1)
const_cast ([[expr.const.cast]](expr.const.cast "7.6.1.11Const cast")),
* [(6.6.2)](#6.6.2)
static_cast ([[expr.static.cast]](expr.static.cast "7.6.1.9Static cast")),
* [(6.6.3)](#6.6.3)
dynamic_cast ([[expr.dynamic.cast]](expr.dynamic.cast "7.6.1.7Dynamic cast")), or
* [(6.6.4)](#6.6.4)
reinterpret_cast ([[expr.reinterpret.cast]](expr.reinterpret.cast "7.6.1.10Reinterpret cast"))
converting, without a user-defined conversion,
a glvalue operand that is one of these expressions
to a glvalue that refers
to the object designated by the operand, or
to its complete object or a subobject thereof,
- [(6.7)](#6.7)
a conditional expression ([[expr.cond]](expr.cond "7.6.16Conditional operator")) that is a glvalue
where the second or third operand is one of these expressions, or
- [(6.8)](#6.8)
a comma expression ([[expr.comma]](expr.comma "7.6.20Comma operator")) that is a glvalue
where the right operand is one of these expressions[.](#6.sentence-2)
[*Example [2](#example-2)*: template<typename T> using id = T;
int i = 1;int&& a = id<int[3]>{1, 2, 3}[i]; // temporary array has same lifetime as aconst int& b = static_cast<const int&>(0); // temporary int has same lifetime as bint&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0); // exactly one of the two temporaries is lifetime-extended — *end example*]
[*Note [4](#note-4)*:
An explicit type conversion ([[expr.type.conv]](expr.type.conv "7.6.1.4Explicit type conversion (functional notation)"), [[expr.cast]](expr.cast "7.6.3Explicit type conversion (cast notation)"))
is interpreted as
a sequence of elementary casts,
covered above[.](#6.sentence-3)
[*Example [3](#example-3)*: const int& x = (const int&)1; // temporary for value 1 has same lifetime as x — *end example*]
— *end note*]
[*Note [5](#note-5)*:
If a temporary object has a reference member initialized by another temporary object,
lifetime extension applies recursively to such a member's initializer[.](#6.sentence-4)
[*Example [4](#example-4)*: struct S {const int& m;};const S& s = S{1}; // both S and int temporaries have lifetime of s — *end example*]
— *end note*]
The exceptions to this lifetime rule are:
- [(6.9)](#6.9)
A temporary object bound to a reference parameter in a function call ([[expr.call]](expr.call "7.6.1.3Function call"))
persists until the completion of the full-expression containing the call[.](#6.9.sentence-1)
- [(6.10)](#6.10)
A temporary object bound to a reference element of
an aggregate of class type initialized from
a parenthesized [*expression-list*](expr.post.general#nt:expression-list "7.6.1.1General[expr.post.general]") ([[dcl.init]](dcl.init "9.5Initializers"))
persists until the completion of the full-expression
containing the [*expression-list*](expr.post.general#nt:expression-list "7.6.1.1General[expr.post.general]")[.](#6.10.sentence-1)
- [(6.11)](#6.11)
A temporary bound to a reference in a [*new-initializer*](expr.new#nt:new-initializer "7.6.2.8New[expr.new]") ([[expr.new]](expr.new "7.6.2.8New")) persists until the completion of the full-expression containing the [*new-initializer*](expr.new#nt:new-initializer "7.6.2.8New[expr.new]")[.](#6.11.sentence-1)
[*Note [6](#note-6)*:
This might introduce a dangling reference[.](#6.11.sentence-2)
— *end note*]
[*Example [5](#example-5)*: struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };
S* p = new S{ 1, {2,3} }; // creates dangling reference — *end example*]
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4886)
The fourth context is when a temporary object
is created in the [*for-range-initializer*](stmt.pre#nt:for-range-initializer "8.1Preamble[stmt.pre]") of
either a range-based for statement
or an enumerating expansion statement ([[stmt.expand]](stmt.expand "8.7Expansion statements"))[.](#7.sentence-1)
If such a temporary object would otherwise be destroyed
at the end of the [*for-range-initializer*](stmt.pre#nt:for-range-initializer "8.1Preamble[stmt.pre]") full-expression,
the object persists for the lifetime of the reference
initialized by the [*for-range-initializer*](stmt.pre#nt:for-range-initializer "8.1Preamble[stmt.pre]")[.](#7.sentence-2)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4896)
The fifth context is when a temporary object is created
in the [*expansion-initializer*](stmt.expand#nt:expansion-initializer "8.7Expansion statements[stmt.expand]") of an iterating or destructuring expansion statement[.](#8.sentence-1)
If such a temporary object would otherwise be destroyed
at the end of that [*expansion-initializer*](stmt.expand#nt:expansion-initializer "8.7Expansion statements[stmt.expand]"),
the object persists for the lifetime of the reference
initialized by the [*expansion-initializer*](stmt.expand#nt:expansion-initializer "8.7Expansion statements[stmt.expand]"), if any[.](#8.sentence-2)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4905)
The sixth context is when a temporary object
is created in a structured binding declaration ([[dcl.struct.bind]](dcl.struct.bind "9.7Structured binding declarations"))[.](#9.sentence-1)
Any temporary objects introduced by
the [*initializer*](dcl.init.general#nt:initializer "9.5.1General[dcl.init.general]")*s* for the variables
with unique names
are destroyed at the end of the structured binding declaration[.](#9.sentence-2)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4913)
Let x and y each be either
a temporary object whose lifetime is not extended, or
a function parameter[.](#10.sentence-1)
If the lifetimes of x and y end at
the end of the same full-expression, andx is initialized before y, then
the destruction of y is sequenced before that of x[.](#10.sentence-2)
If the lifetime of two or more temporaries
with lifetimes extending beyond the full-expressions in which they were created
ends at the same point,
these temporaries are destroyed at that point in the reverse order of the
completion of their construction[.](#10.sentence-3)
In addition, the destruction of such temporaries shall
take into account the ordering of destruction of objects with static, thread, or
automatic storage duration ([[basic.stc.static]](basic.stc.static "6.8.6.2Static storage duration"), [[basic.stc.thread]](basic.stc.thread "6.8.6.3Thread storage duration"), [[basic.stc.auto]](basic.stc.auto "6.8.6.4Automatic storage duration"));
that is, ifobj1 is an object with the same storage duration as the temporary and
created before the temporary is created
the temporary shall be destroyed beforeobj1 is destroyed;
ifobj2 is an object with the same storage duration as the temporary and
created after the temporary is created
the temporary shall be destroyed afterobj2 is destroyed[.](#10.sentence-4)
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L4944)
[*Example [6](#example-6)*: struct S { S();
S(int); friend S operator+(const S&, const S&); ~S();};
S obj1;const S& cr = S(16)+S(23);
S obj2;
The expressionS(16) + S(23) creates three temporaries:
a first temporaryT1 to hold the result of the expressionS(16),
a second temporaryT2 to hold the result of the expressionS(23),
and a third temporaryT3 to hold the result of the addition of these two expressions[.](#11.sentence-1)
The temporaryT3 is then bound to the referencecr[.](#11.sentence-2)
It is unspecified whetherT1 orT2 is created first[.](#11.sentence-3)
On an implementation whereT1 is created beforeT2,T2 shall be destroyed beforeT1[.](#11.sentence-4)
The temporariesT1 andT2 are bound to the reference parameters ofoperator+;
these temporaries are destroyed at the end of the full-expression
containing the call tooperator+[.](#11.sentence-5)
The temporaryT3 bound to the referencecr is destroyed at the end ofcr's
lifetime, that is, at the end of the program[.](#11.sentence-6)
In addition, the order in whichT3 is destroyed takes into account the destruction order of other objects with
static storage duration[.](#11.sentence-7)
That is, becauseobj1 is constructed beforeT3,
andT3 is constructed beforeobj2,obj2 shall be destroyed beforeT3,
andT3 shall be destroyed beforeobj1[.](#11.sentence-8)
— *end example*]
[25)](#footnote-25)[25)](#footnoteref-25)
The same rules apply to initialization of an initializer_list object ([[dcl.init.list]](dcl.init.list "9.5.5List-initialization")) with its
underlying temporary array[.](#footnote-25.sentence-1)