受Preney在表达式模板与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引用成员X::t绑定到其他东西。
好的,从这个答案const引用rvalue的类数据成员的生存期是多少?,我知道这是预期的行为。
然而,这里的问题是:Preney在表达式模板与C++11中允许临时向量存在的代码有什么不同,只要rvalue引用成员存在?参见他的案例2,在那里临时人员是被创造出来的。
显然,这里使用的是同样的构造,但我可能遗漏了一些东西。
编辑:基于以下R. Martinho Fernandes的回答,我尝试了以下几点:
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因此,显然存储在表达式模板中的引用不会悬空。这里发生什么事情?
发布于 2013-08-07 11:51:59
Preney在表达式模板和C++11中的代码有什么不同,只要rvalue-引用成员存在,就允许临时向量存在?
没有什么能允许这样的事情发生。
那里的临时向量一直存在到完整表达式的末尾,就像没有绑定到本地引用变量的任何其他临时向量一样。在Paul的代码中,这就足够了,因为代码立即将表达式树具体化为一个实际的math_vector,在此之后就不再需要临时人员了。
Paul的代码不会在任何地方存储任何表达式模板节点(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发布于 2013-08-07 13:23:36
正如R. Martinho Fernandes在他的回答中所指出的那样,问题在于你在你的“表达式树”中捕获了对rvalue的引用,这比它们的引用更持久。解决方案是只存储对rvalue的引用,并捕获由rvalue引用直接传递的对象。换句话说,将它们的值存储在表达式树中,而不是通过引用(Coliru的实时代码):
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引用,则她有责任确保引用超过表达式树对象。
https://stackoverflow.com/questions/18102501
复制相似问题