Baz
is a class with a constructor. Therefore, when you use list initialization, the compiler will look for a constructor to call. That constructor will be passed the members of the braced-init-list, or a std::initializer_list
if one matches the members of the list. In either case, the rules of temporary binding to function parameters are in effect ([class.temporary]/6.1):
A temporary object bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
However, Bar
is not a class with a constructor; it is an aggregate. Therefore, when you use list initialization, you (in this case) invoke aggregate initialization. And therefore, the member reference will be bound to the given prvalue directly. The rule for that is ([class.temporary]/6):
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
Followed by 3 exceptions which do not apply to this case (including 6.1, quoted above).
The lifetime of the reference Bar::foo
extends to the end of main
. Which doesn't happen until cin.get()
returns.
How can I implement the constructor of Baz correctly?
If by "correctly", you mean "like Bar
", you cannot. Aggregates get to do things that non-aggregates can't; this is one of them.
It's similar to this:
struct Types { int i; float f };
using Tpl = std::tuple<int, float>;
int &&i1 = Types{1, 1.5}.i;
int &&i2 = std::get<0>(Tpl{1, 1.5});
i2
is a dangling reference to a subobject of a destroyed temporary. i1
is a reference to a subobject of a temporary whose lifetime was extended.
There are some things you just can't do through functions.