Член данных и время жизни

Каким-то образом вдохновлен кодом шаблона выражения в Шаблоны выражений и C ++ 11, написанный Полом Преней, я решил проверить следующее:

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));
}

Затем я использовал его для создания экземпляра X<const vector<int>&> а также X<vector<int>&&> следующее:

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;
}

Выход:

x1: 1 2 3 4
x2: 0 0 33 0 0 0 7 8

который показывает, что время жизни временного vector<int>{5,6,7,8} не расширяется, а элемент rvalue-reference X::t привязывается к чему-то еще.

Хорошо из этого ответа Каково время жизни члена данных класса, который является ссылкой на значение?, Я знаю, что это ожидаемое поведение.

Однако здесь возникает вопрос: что отличается в коде Пола Преней в Шаблоны выражений и C ++ 11 что позволяет временным векторам существовать до тех пор, пока существуют члены-ссылки-значения? Смотрите его Случай 2, где создаются временные.

По-видимому, здесь используется та же самая конструкция, но я, вероятно, что-то упускаю.


Редактировать:
Основываясь на ответе Р. Мартиньо Фернандеса ниже, я попробовал следующее:

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;
}

и получается, что это правильный код, который выводит:

vec1: 1.0 1.1 1.2
vec2: 2.0 2.1 2.2

Поэтому, очевидно, ссылки, хранящиеся в шаблоне выражения, не висят. Что здесь происходит?

1

Решение

Чем отличается код Пола Преней в шаблонах Expression и C ++ 11, который позволяет существовать временным векторам до тех пор, пока существуют члены rvalue-reference?

Ничто не допускает такой вещи.

Временные векторы существуют до конца полного выражения, как и любой другой временный вектор, который не связан с локальной ссылочной переменной. Этого достаточно в коде Пола, потому что код сразу материализует дерево выражений в фактическое math_vectorи временные не нужны больше после этого.

Код Пола не хранит ни одного узла шаблона выражения (math_vector_expr) где угодно, пока ваш код хранит один (X) как x2, Это известная проблема с auto: он делает неправильные вещи, когда вы используете шаблоны выражений, потому что это приводит к сохранению дерева выражений, которое, вероятно, будет содержать ссылки, которые сразу же будут зависать.

Чтобы сделать это совершенно ясным, хорошо следующее.

math_vector<3> result =
math_vector<3>{1.0, 1.1, 1.2} +
math_vector<3>{2.0, 2.1, 2.2} +
math_vector<3>{3.0, 3.1, 3.2} +
math_vector<3>{4.0, 4.1, 4.2}
; // no references are held to any temporaries past this point

Следующее не хорошо.

math_vector_expr<3> result =        // or auto
math_vector<3>{1.0, 1.1, 1.2} +
math_vector<3>{2.0, 2.1, 2.2} +
math_vector<3>{3.0, 3.1, 3.2} +
math_vector<3>{4.0, 4.1, 4.2}
; // result now holds references to those temporaries
3

Другие решения

Проблема, как утверждает Р. Мартиньо Фернандес в своем ответе, заключается в том, что вы фиксируете ссылки на значения в вашем «дереве выражений», которые переживают их референты. Решение состоит в том, чтобы хранить только ссылки на lvalue и захватывать объекты, непосредственно переданные по rvalue. Другими словами, храните их ценности в дереве выражений вместо ссылки (Живой код в Колиру):

template<typename T>
struct X
{
X(T t) : t(std::move(t)) {}

T t;
};

// Capture lvalue references
template<typename T>
X<const T&> CreateX(const T& t)
{
return X<const T&>(t);
}

// Copy rvalue references
template<typename T>
typename std::enable_if<std::is_rvalue_reference<T&&>::value,X<T>>::type
CreateX(T&& t)
{
return X<T>(std::move(t));
}

Если пользователь передает вам ссылку lvalue, он несет ответственность за то, чтобы референт пережил объект дерева выражений.

2

По вопросам рекламы [email protected]