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

18 KiB
Raw Permalink Blame History

[class.temporary]

6 Basics [basic]

6.8 Memory and objects [basic.memobj]

6.8.7 Temporary objects [class.temporary]

1

#

A temporary object is an object created

when a prvalue is converted to an xvalue ([conv.rval]) and

when needed by the implementation to pass or return an object of suitable type (see below).

Even when the creation of the temporary object is unevaluated ([expr.context]), all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed.

[Note 1:

This includes accessibility ([class.access]) and whether it is deleted, for the constructor selected and for the destructor.

However, in the special case of the operand of adecltype-specifier ([dcl.type.decltype]), no temporary is introduced, so the foregoing does not apply to such a prvalue.

— end note]

2

#

The materialization of a temporary object is generally delayed as long as possible in order to avoid creating unnecessary temporary objects.

[Note 2:

Temporary objects are materialized:

when binding a reference to a prvalue ([dcl.init.ref], [expr.type.conv], [expr.dynamic.cast], [expr.static.cast], [expr.const.cast], [expr.cast]),

when performing certain member accesses on a class prvalue ([expr.ref], [expr.mptr.oper]),

when invoking an implicit object member function on a class prvalue ([expr.call]),

when performing an array-to-pointer conversion or subscripting on an array prvalue ([conv.array], [expr.sub]),

when initializing an object of type std::initializer_list from a braced-init-list ([dcl.init.list]),

for certain unevaluated operands ([expr.typeid], [expr.sizeof]), and

when a prvalue that has type other than cv void appears as a discarded-value expression ([expr.context]).

— end note]

[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.

Likewise,f()'s result is constructed directly in b andg()'s result is constructed directly in c.

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.

— end example]

3

#

When an object of class type X is passed to or returned from a potentially-evaluated function call, if X is

a scalar type or

a class type that has at least one eligible copy or move constructor ([special]), 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)

    The first such temporary object is constructed from the function argument or return value, respectively.

  • (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.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.

(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).

[Note 3:

This latitude is granted to allow objects to be passed to or returned from functions in registers.

— end note]

4

#

Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.

This is true even if that evaluation ends in throwing an exception.

Thevalue computations andside effects of destroying a temporary object are associated only with the full-expression, not with any specific subexpression.

5

#

There are several contexts in which temporaries are destroyed at a different point than the end of the full-expression.

The first context is when a default constructor is called to initialize an element of an array with no corresponding initializer ([dcl.init]).

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], [class.copy.ctor]).

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.

6

#

The third context is when a reference binds to a temporary object.25

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:

a temporary materialization conversion ([conv.rval]),

( expression ), where expression is one of these expressions,

subscripting ([expr.sub]) of an array operand, where that operand is one of these expressions,

a class member access ([expr.ref]) 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,

a pointer-to-member operation ([expr.mptr.oper]) 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,

a

const_cast ([expr.const.cast]),

static_cast ([expr.static.cast]),

dynamic_cast ([expr.dynamic.cast]), or

reinterpret_cast ([expr.reinterpret.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,

a conditional expression ([expr.cond]) that is a glvalue where the second or third operand is one of these expressions, or

a comma expression ([expr.comma]) that is a glvalue where the right operand is one of these expressions.

[Example 2: template 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:

An explicit type conversion ([expr.type.conv], [expr.cast]) is interpreted as a sequence of elementary casts, covered above.

[Example 3: const int& x = (const int&)1; // temporary for value 1 has same lifetime as x — end example]

— end note]

[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.

[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)

    A temporary object bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.

  • (6.10)

    A temporary object bound to a reference element of an aggregate of class type initialized from a parenthesized expression-list ([dcl.init]) persists until the completion of the full-expression containing the expression-list.

  • (6.11)

    A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer. [Note 6: This might introduce a dangling reference. — end note] [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

#

The fourth context is when a temporary object is created in the for-range-initializer of either a range-based for statement or an enumerating expansion statement ([stmt.expand]).

If such a temporary object would otherwise be destroyed at the end of the for-range-initializer full-expression, the object persists for the lifetime of the reference initialized by the for-range-initializer.

8

#

The fifth context is when a temporary object is created in the expansion-initializer of an iterating or destructuring expansion statement.

If such a temporary object would otherwise be destroyed at the end of that expansion-initializer, the object persists for the lifetime of the reference initialized by the expansion-initializer, if any.

9

#

The sixth context is when a temporary object is created in a structured binding declaration ([dcl.struct.bind]).

Any temporary objects introduced by the initializers for the variables with unique names are destroyed at the end of the structured binding declaration.

10

#

Let x and y each be either a temporary object whose lifetime is not extended, or a function parameter.

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.

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.

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.thread], [basic.stc.auto]); 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.

11

#

[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.

The temporaryT3 is then bound to the referencecr.

It is unspecified whetherT1 orT2 is created first.

On an implementation whereT1 is created beforeT2,T2 shall be destroyed beforeT1.

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+.

The temporaryT3 bound to the referencecr is destroyed at the end ofcr's lifetime, that is, at the end of the program.

In addition, the order in whichT3 is destroyed takes into account the destruction order of other objects with static storage duration.

That is, becauseobj1 is constructed beforeT3, andT3 is constructed beforeobj2,obj2 shall be destroyed beforeT3, andT3 shall be destroyed beforeobj1.

— end example]

25)25)

The same rules apply to initialization of an initializer_list object ([dcl.init.list]) with its underlying temporary array.