数据成员和值生存时间
Data member and rvalue life-time
受Paul Preney编写的expression templates和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}
的寿命没有被延长并且右值参考成员X::t
与其他东西结合。
好的,根据这个答案,常量引用右值的类数据成员的生存期是多少?,我知道这是意料之中的行为。
然而,这里的问题是:Paul Preney的表达式模板和C++11中的代码有什么不同,只要右值引用成员存在,就允许临时向量存在?参见他的案例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
因此,存储在表达式模板中的引用显然不是悬空的。这是怎么回事?
Paul Preney在Expression模板和C++11中的代码有什么不同,只要右值引用成员存在,就允许临时向量存在?
任何事情都不允许这样做。
那里的临时向量一直存在到完整表达式结束,就像任何其他未绑定到局部引用变量的临时向量一样。这在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
正如R.Martinho Fernandes在他的回答中所说,问题是你在你的"表达式树"中捕获对右值的引用,这些引用比它们的引用更持久。解决方案是只存储对左值的引用,并直接捕获由右值引用传递的对象。换句话说,将它们的值存储在表达式树中,而不是通过引用(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));
}
如果用户向您传递了一个左值引用,则她有责任确保该引用的寿命超过表达式树对象。
- 用于访问容器<T>数据成员的正确 API
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 数据成员SFINAE的C++17测试:gcc vs clang
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 如何在c++中定义以struct为数据成员的类中的构造函数
- 静态数据成员模板专用化的实例化点在哪里
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 使用指针访问数组中的对象数据成员
- 友元函数无法访问私有数据成员 (c++)
- 我可以在 C++ 中将数据成员/变量从其定义之外添加到结构中吗?
- 为什么将一个结构的引用设置为等于另一个结构只会更改一个数据成员?
- 将私有数据成员添加到野牛生成的类中
- 输入数据成员未按要求工作
- 二维矢量数据成员
- 在类 A 中创建类型为 B 类的向量 - 访问数据 [C++] [成员在两个类中都是私有的]
- 调用在 HXX 文件中声明的静态数据成员
- 是否可以根据其数据成员的类型确定类型的大小
- 访问数据成员(本身是对象)的数据成员,就好像它们是类成员一样
- 使公共数据成员在C++中无法访问
- 数据成员和值生存时间