struct A {
A(int) : i(new int(783)) {
std::cout << \"a ctor\" << std::endl;
}
A(const A& other) : i(new int(*(other.i))) {
Rules of temporary lifetime extension for each specific context are explicitly spelled out in the language specification. And it says that
12.2 Temporary objects
5 The second context is when a reference is bound to a temporary. [...] A temporary bound to the returned value in a function return statement (6.6.3) persists until the function exits. [...]
Your temporary object is destroyed at the moment of function exit. That happens before the initialization of the recipient object begins.
You seem to assume that your temporary should somehow live longer than that. Apparently you are trying to apply the rule that says that the temporary should survive until the end of the full expression. But that rule does not apply to temporaries created inside functions. Such temporaries' lifetimes are governed by their own, dedicated rules.
Both your foo and your foo_2 produce undefined behavior, if someone attempts to use the returned reference.