김선달*_*김선달 1 c++ templates reference
我写了一个表达式模板代码,但出现错误。
以下只是一个最小的可重现示例。
#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;
}
Run Code Online (Sandbox Code Playgroud)
当我尝试使用添加超过 3秒的VectorSum<...>
( sum
in main()
)Vector
时,发布版本中会发生以下错误。
Exception 0xc0000005 encountered at address 0x7ff76f8a1189: Access violation reading location 0x00000000
Run Code Online (Sandbox Code Playgroud)
所以我像上面一样连接了指针打印机,然后它说
00000067C1EFF920
0000000000000000
Process finished with exit code -1073741819 (0xC0000005)
Run Code Online (Sandbox Code Playgroud)
如果我连接更多打印机(即:inoperator +
或VecSum
ctor,...),那么突然一切正常。
谁能告诉我为什么会发生这个错误?
v + v
是一个类型的表达式VectorSum<Vector<16>, Vector<16>>
。
v + v + v
是 type 的表达式VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
,其中第一个模板参数的类型很重要:
VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (B)
// ^^^^^^^^^ (A)
Run Code Online (Sandbox Code Playgroud)
与此特化相关联的对象A
存储一个类型为 const 的引用B const&
,但是一旦表达式v + v + v
过期,子表达式的生命周期v + v
也会过期,这意味着您将在表达式对象中存储一个悬空引用(A)
(作为数据成员lhs
)。
仅存储表达式的表达式模板时您没有看到此问题的原因v + v
是,关联的专业化对象的thelhs
和rhs
data 成员都指的是v
生命周期延伸到main()
.
最后,在使用占位符类型(例如 )将表达式模板存储在中间变量中时要小心auto
,因为它会 1) 可能会产生生命周期问题,例如上面的问题,并且 2) 可能是不经意的读者意想不到的,因为可能期望(与std::vector<bool>::operator[]
))auto = v + v
产生decltype(v)
(即 a Vector<16>
)而不是代理类型,该类型应该对客户端隐藏。