Somehow inspired by the expression template code in Expression templates and C++11, written by Paul Preney, I decided to test the following:
template<typename T>
struct X
{
X(T t) : t(std::forward<T>(t)) {}
T t;
};
template<typename T>
auto CreateX(T&& t) -> X<decltype(std::forward<T>(t))>
{
return X<decltype(std::forward<T>(t))>(std::forward<T>(t));
}
Then, I used it to produce an instance of X<const vector<int>&>
and X<vector<int>&&>
as follows:
int main()
{
int vec = {1,2,3,4};
auto x1 = CreateX(vec);
auto x2 = CreateX(vector<int>{5,6,7,8});
cout << "x1: "; for(auto x : x1.t) cout << x << " "; cout << endl;
cout << "x2: "; for(auto x : x2.t) cout << x << " "; cout << endl;
}
The output is:
x1: 1 2 3 4
x2: 0 0 33 0 0 0 7 8
which shows that the life-time of the temporary vector<int>{5,6,7,8}
is not being extended, and the rvalue-reference member X::t
binds to something else.
Okay, from this answer What is the lifetime of the class data member which const reference to a rvalue?, I know this is the expected behaviour.
However, the question here is: what is different in Paul Preney' code in Expression templates and C++11 that permits the temporary vectors to exist as long as the rvalue-references members exist? See his Case 2, where temporaries are created.
Apparently, the same construct there is used here, but I am probably missing something.
Edit: Based on the answer of R. Martinho Fernandes below, I tried the following:
int main()
{
using namespace std;
auto expr = math_vector<3>{1.0, 1.1, 1.2} + math_vector<3>{2.0, 2.1, 2.2};
cout << "vec1: "; for(int i = 0; i < 3; ++i) cout << expr.le()[i] << " "; cout << endl;
cout << "vec2: "; for(int i = 0; i < 3; ++i) cout << expr.re()[i] << " "; cout << endl;
}
and it turns out that this is a valid code that outputs:
vec1: 1.0 1.1 1.2
vec2: 2.0 2.1 2.2
Therefore, apparently references stored in the expression template is not dangling. What is going on here?