引用在表达式模板中变得悬空
Reference becomes dangling in expression template
我写了一个表达式模板代码,但是我有一个错误。
以下只是一个最小的可重现示例。
#include <iostream>
template<typename Derived>
class VecExp {
public:
double operator[](int i) const {return static_cast<Derived const&>(*this)[i];}
};
template<int n>
class Vector : public VecExp<Vector<n>> {
public:
template<int m>
Vector(const double(&arr)[m]) {
static_assert(m==n, "");
for(int i=0; i<m; ++i) data[i] = arr[i];
}
template<typename E>
Vector(const VecExp<E>& exp) {
for(int i=0; i<n; ++i) data[i] = exp[i];
}
double operator[](int i) const {std::cout<<this<<std::endl; return data[i];}
double& operator[](int i) {return data[i];}
double data[n];
};
template<typename E1, typename E2>
class VectorSum : public VecExp<VectorSum<E1, E2>>{
E1 const& lhs;
E2 const& rhs;
public:
VectorSum(E1 const& lhs, E2 const& rhs) : lhs(lhs), rhs(rhs) {
// std::cout << &(this->lhs) << ", " << &(this->rhs) << std::endl;
}
double operator[](int i) const {return lhs[i] + rhs[i];}
};
template<typename E1, typename E2>
VectorSum<E1, E2>
operator + (VecExp<E1> const& lhs, VecExp<E2> const& rhs) {
return VectorSum<E1, E2>(*static_cast<const E1*>(&lhs), *static_cast<const E2*>(&rhs));
}
int main() {
Vector<16> v({1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16});
std::cout << &v << std::endl;
const auto sum = v + v + v + v;
Vector<16> v2 = sum; // error at here
std::cout << sum[0] << std::endl;
return 0;
}
当我尝试使用 VectorSum<...>
(sum
in main()
) 添加超过 3 Vector
s 时,在发布版本中会发生以下错误。
Exception 0xc0000005 encountered at address 0x7ff76f8a1189: Access violation reading location 0x00000000
所以我像上面那样连接了指针打印机,然后它说
00000067C1EFF920
0000000000000000
Process finished with exit code -1073741819 (0xC0000005)
如果我连接更多打印机(即:在 operator +
或 VecSum
ctor 中,...),然后突然一切正常。
谁能告诉我为什么会出现这个错误?
在 Windows、Visual Studio community 2019 上安装了 CLion。
v + v
是 VectorSum<Vector<16>, Vector<16>>
.
类型的表达式
v + v + v
是类型 VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
的表达式,其中第一个模板参数的类型很重要:
VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (B)
// ^^^^^^^^^ (A)
与此特化关联的对象 A
存储类型 B const&
的 const 引用,但是一旦表达式 v + v + v
过期,子表达式 v + v + v
的生命周期也会过期=11=],这意味着您在表达式对象 (A)
中存储了一个悬空引用(作为数据成员 lhs
)。
仅存储表达式 v + v
的表达式模板时没有看到此问题的原因是相关专业化对象的 lhs
和 rhs
数据成员都引用对象 v
的生命周期延长至 main()
.
最后,在使用 auto
等占位符类型将表达式模板存储在中间变量中时要小心,因为它 1) 可能会产生生命周期问题,例如上述问题,并且 2) 可能会出乎意料的 reader,正如人们所期望的(与 std::vector<bool>::operator[]
比较))auto = v + v
会导致 decltype(v)
(即 Vector<16>
)而不是应该对所有客户端隐藏的代理类型。
我写了一个表达式模板代码,但是我有一个错误。
以下只是一个最小的可重现示例。
#include <iostream>
template<typename Derived>
class VecExp {
public:
double operator[](int i) const {return static_cast<Derived const&>(*this)[i];}
};
template<int n>
class Vector : public VecExp<Vector<n>> {
public:
template<int m>
Vector(const double(&arr)[m]) {
static_assert(m==n, "");
for(int i=0; i<m; ++i) data[i] = arr[i];
}
template<typename E>
Vector(const VecExp<E>& exp) {
for(int i=0; i<n; ++i) data[i] = exp[i];
}
double operator[](int i) const {std::cout<<this<<std::endl; return data[i];}
double& operator[](int i) {return data[i];}
double data[n];
};
template<typename E1, typename E2>
class VectorSum : public VecExp<VectorSum<E1, E2>>{
E1 const& lhs;
E2 const& rhs;
public:
VectorSum(E1 const& lhs, E2 const& rhs) : lhs(lhs), rhs(rhs) {
// std::cout << &(this->lhs) << ", " << &(this->rhs) << std::endl;
}
double operator[](int i) const {return lhs[i] + rhs[i];}
};
template<typename E1, typename E2>
VectorSum<E1, E2>
operator + (VecExp<E1> const& lhs, VecExp<E2> const& rhs) {
return VectorSum<E1, E2>(*static_cast<const E1*>(&lhs), *static_cast<const E2*>(&rhs));
}
int main() {
Vector<16> v({1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16});
std::cout << &v << std::endl;
const auto sum = v + v + v + v;
Vector<16> v2 = sum; // error at here
std::cout << sum[0] << std::endl;
return 0;
}
当我尝试使用 VectorSum<...>
(sum
in main()
) 添加超过 3 Vector
s 时,在发布版本中会发生以下错误。
Exception 0xc0000005 encountered at address 0x7ff76f8a1189: Access violation reading location 0x00000000
所以我像上面那样连接了指针打印机,然后它说
00000067C1EFF920
0000000000000000
Process finished with exit code -1073741819 (0xC0000005)
如果我连接更多打印机(即:在 operator +
或 VecSum
ctor 中,...),然后突然一切正常。
谁能告诉我为什么会出现这个错误?
在 Windows、Visual Studio community 2019 上安装了 CLion。
v + v
是 VectorSum<Vector<16>, Vector<16>>
.
v + v + v
是类型 VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
的表达式,其中第一个模板参数的类型很重要:
VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (B)
// ^^^^^^^^^ (A)
与此特化关联的对象 A
存储类型 B const&
的 const 引用,但是一旦表达式 v + v + v
过期,子表达式 v + v + v
的生命周期也会过期=11=],这意味着您在表达式对象 (A)
中存储了一个悬空引用(作为数据成员 lhs
)。
仅存储表达式 v + v
的表达式模板时没有看到此问题的原因是相关专业化对象的 lhs
和 rhs
数据成员都引用对象 v
的生命周期延长至 main()
.
最后,在使用 auto
等占位符类型将表达式模板存储在中间变量中时要小心,因为它 1) 可能会产生生命周期问题,例如上述问题,并且 2) 可能会出乎意料的 reader,正如人们所期望的(与 std::vector<bool>::operator[]
比较))auto = v + v
会导致 decltype(v)
(即 Vector<16>
)而不是应该对所有客户端隐藏的代理类型。