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?
Nothing permits such a thing.
The temporary vectors there exist until the end of the full expression, as any other temporary that isn't bound to a local reference variable. That is enough in Paul's code because the code immediately materialises the expression tree into an actual
math_vector
, and the temporaries are not needed anymore after that.Paul's code does not store any expression template node (
math_vector_expr
) anywhere, while your code stores one (X
) asx2
. That's a known issue withauto
: it does the wrong thing when you are using expression templates because it results in storing the expression tree, which is likely to contain references that will become dangling right away.In order to make it totally clear, the following is fine.
The following is not fine.