Каким-то образом вдохновлен кодом шаблона выражения в Шаблоны выражений и 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
Поэтому, очевидно, ссылки, хранящиеся в шаблоне выражения, не висят. Что здесь происходит?
Чем отличается код Пола Преней в шаблонах 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
Проблема, как утверждает Р. Мартиньо Фернандес в своем ответе, заключается в том, что вы фиксируете ссылки на значения в вашем «дереве выражений», которые переживают их референты. Решение состоит в том, чтобы хранить только ссылки на 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, он несет ответственность за то, чтобы референт пережил объект дерева выражений.