What is the lifetime of temporary arguments in nested function calls?

138 Views Asked by At

Consider the program:

#include <print>
#include <string>

struct X
{
    std::string tag;

    ~X() { std::println("X::~X() - {}", tag); }
};

X f(const X& x) {
    std::println("f() - {}", x.tag);
    return {x.tag + "!"};
}

int main() {
    f(f({"x"}));
}

For a confoming C++(23) compiler, am I correct to assume the following output:

f() - x
f() - x!
X::~X() - x!!
X::~X() - x!
X::~X() - x

Meaning the lifetime of the temporary objects referenced through function parameters ends after running the full expression (1), and the temporaries are destroyed in reverse order of creation (2).

Note that f() takes X by reference. If it were declared X f(X x) instead, then the X object may in fact be destroyed as soon as the function returns (3):

f() - x
X::~X() - x
f() - x!
X::~X() - x!
X::~X() - x!!

I think these are the relevant quotes from the standard:

  1. [class.temporary#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.

  1. [class.temporary#8]

The destruction of a temporary whose lifetime is not extended beyond the full-expression in which it was created is sequenced before the destruction of every temporary which is constructed earlier in the same full-expression.

  1. [expr.call#6]

It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.

1

There are 1 best solutions below

2
Valentin Milea On

the lifetime of the temporary objects referenced through function parameters ends after running the full expression (1), and the temporaries are destroyed in reverse order of creation (2).

That's correct. According to the C++ standard:

  1. [class.temporary#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.

  1. [class.temporary#8]

The destruction of a temporary whose lifetime is not extended beyond the full-expression in which it was created is sequenced before the destruction of every temporary which is constructed earlier in the same full-expression.