3

Please see the following code examples:

(A)

#include <iostream>
#include <utility>
struct wrapper {
  int&& x;
};

void f(wrapper a) { std::cout << std::move(a.x) << std::endl; }
int g() { return 0; }

int main() {
  f(wrapper{g()});
}

(B)

#include <iostream>
#include <utility>
struct wrapper {
  int&& x;
};
wrapper make_wrapper(int&& x) {
  return{ std::move(x) };
}

void f(wrapper a) { std::cout << std::move(a.x) << std::endl; }
int g() { return 0; }

int main() {
  f(make_wrapper(g()));
}

Are these examples valid? That is, are temporaries created by g alive until the execution of f finishes? What happens when f takes its argument as a reference?

Junekey Jeon
  • 1,496
  • 1
  • 11
  • 18
  • Is there any particular doubt you have that the general life-time rule for temporaries applies or is sufficient? (i.e. lifetime ends at the end of the full-expression creating the temporary) – walnut Sep 08 '19 at 20:34
  • Uhm.. I think I do not fully understand the general rule. I once thought that lifetime for a temporary is ended with the semicolon, but I've once encountered that this kind of argument wrapping led me to a problem. I don't recall what exactly was the problem, though. Maybe I just misunderstood about the problem? – Junekey Jeon Sep 08 '19 at 21:13
  • Possible duplicate of [C++: Life span of temporary arguments?](https://stackoverflow.com/questions/2506793/c-life-span-of-temporary-arguments) – JaMiT Sep 08 '19 at 22:42

1 Answers1

4

Both code examples are valid.

The only temporaries created in your code are the int returned from g(), the wrapper returned from make_wrapper and the wrapper created via aggregate-initialization in wrapper{...}.

All of these happen lexically in the full-expression

f(wrapper{g()});

or

f(make_wrapper(g()));

and in general temporaries' lifetime ends at the end of the full-expression that they were created in. Therefore all your temporaries live until the end of the respective line throughout the call to f, and none of them are used outside their lifetime.

There are only few exceptions to this lifetime rule and all of them extend the lifetime. In fact binding the temporary int returned from g() to a int&& subobject via aggregate-initialization actually extends the lifetime of the temporary to that of the reference.

Therefore

wrapper w{g()};

would actually not have a dangling reference member, even in following statements.

walnut
  • 21,629
  • 4
  • 23
  • 59