Is the code correct or is it an undefined behavior?
The class definition itself is correct, however use of it (or any other class which has a reference member variable) is very prone to have a dangling reference, and thus an undefined behavior if proper guard logic is not implemented. E.g. in your code, this line:
std::cout << t_by_value.val << " " << t_by_ref.val << "\n";
Refers to a dangling reference val
of t_by_value
, and thus is UB.
EDIT: Thanks to apple apple's comment, there was pointed out another exception to this rule, which requires to bind the reference member data type directly when the class has aggregate initialization, [class.temporary]/6.10:
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 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.
Thus the code snippet provided in the question is 100% correct and doesn't have any undefined behaviour.
where this long double is stored, since it is not contained in object test<long double>
?
In your scenario it's stored in a temporary passed to the aggregate constructor here (and which lifetime is extended to the end of the enclosing scope):
test<long double> t_by_value{get()}; // THIS IS A CONTAINER
When I stated that test<long double>
is a container and test<long double&>
is a view, is correct?
Not in this case. In order to understand it better, you need to take into account how templates deduce their types. When you have a template variable declared like this:
T&& val;
You have so-called universal reference, which can be only one of two types: an rvalue reference or an lvalue reference. For any lvalue the type turns into T&
for any xvalue or prvalue it turns into T&&
.
EDIT: in this case it's not a universal reference, so the references collapsed a little bit differently:
- if
T
is passed by value (e.g. int
) -> T&&
- if
T
is a lvalue reference (e.g. int&
) -> T&
- if
T
is another type of reference (e.g. int&&
) -> T&&