使用通过 auto 关键字创建的类型的表达式模板中的分段错误
Segmentation fault in expression template using type created with auto keyword
我正在使用计算内核的表达式模板构建代码。我的问题很简短:为什么 GNU G++ 在以下示例中包含 +=
的行上给出段错误(4.9.1,使用 -O3 编译):
// Like this it crashes
auto expression = Ix_h( Ix(u) );
ut += expression;
但当我输入等效代码时却不是:
// But like this it does not
ut += Ix_h( Ix(u) );
Clang 和 Intel 都工作正常。
我已经在下面添加了完整的代码。抱歉太长了,这是我能创造的最短的例子:
struct Grid
{
Grid(const int itot, const int gc) :
itot(itot), gc(gc), istart(gc), iend(itot+gc), icells(itot+2*gc) {}
const int itot;
const int gc;
const int istart;
const int iend;
const int icells;
};
template<int loc, class Inner>
struct Interp
{
Interp(const Inner& inner) : inner_(inner) {}
const Inner& inner_;
inline double operator()(const int i) const
{
return (-1./16)*(inner_(i + (-2+loc)) + inner_(i + ( 1+loc)))
+ ( 9./16)*(inner_(i + (-1+loc)) + inner_(i + ( loc)));
}
};
template<class Inner>
inline Interp<1, Inner> Ix(const Inner& inner)
{ return Interp<1, Inner>(inner); }
template<class Inner>
inline Interp<0, Inner> Ix_h(const Inner& inner)
{ return Interp<0, Inner>(inner); }
class Field
{
public:
Field(const Grid& grid) :
grid_(grid),
data_(new double[grid_.icells]) {}
inline double operator()(const int i) const
{ return data_[i]; }
inline double& operator()(const int i)
{ return data_[i]; }
template<class T>
inline Field& operator+=(const T& expression)
{
for (int i=grid_.istart; i<grid_.iend; ++i)
(*this)(i) += expression(i);
return *this;
}
private:
const Grid& grid_;
double* data_;
};
int main()
{
Grid grid(256, 4);
Field u (grid);
Field ut(grid);
// Like this it crashes
auto expression = Ix_h( Ix(u) );
ut += expression;
// But like this it does not
ut += Ix_h( Ix(u) );
return 0;
}
auto expression = Ix_h( Ix(u) );
此处,Ix(u)
创建了一个绑定到转换构造函数 Interp<0, Interp<1, Field>>::Interp(Inner const&)
引用的临时对象。构造函数初始化对象的引用inner_
。您现在有一个临时值的引用,该值将在完整表达式 Ix_h( Ix(u) )
.
的末尾被破坏
它在您执行 ut += Ix_h( Ix(u) )
时起作用的原因是因为引用和临时变量在表达式末尾消失。初始化 expression
只是传递引用。然后使用 ut += expression
使用一个已经死亡的对象,这是未定义的行为。
解决方案:使 inner_
成为对象而不是引用,以便发生复制:
Inner inner_;
我正在使用计算内核的表达式模板构建代码。我的问题很简短:为什么 GNU G++ 在以下示例中包含 +=
的行上给出段错误(4.9.1,使用 -O3 编译):
// Like this it crashes
auto expression = Ix_h( Ix(u) );
ut += expression;
但当我输入等效代码时却不是:
// But like this it does not
ut += Ix_h( Ix(u) );
Clang 和 Intel 都工作正常。
我已经在下面添加了完整的代码。抱歉太长了,这是我能创造的最短的例子:
struct Grid
{
Grid(const int itot, const int gc) :
itot(itot), gc(gc), istart(gc), iend(itot+gc), icells(itot+2*gc) {}
const int itot;
const int gc;
const int istart;
const int iend;
const int icells;
};
template<int loc, class Inner>
struct Interp
{
Interp(const Inner& inner) : inner_(inner) {}
const Inner& inner_;
inline double operator()(const int i) const
{
return (-1./16)*(inner_(i + (-2+loc)) + inner_(i + ( 1+loc)))
+ ( 9./16)*(inner_(i + (-1+loc)) + inner_(i + ( loc)));
}
};
template<class Inner>
inline Interp<1, Inner> Ix(const Inner& inner)
{ return Interp<1, Inner>(inner); }
template<class Inner>
inline Interp<0, Inner> Ix_h(const Inner& inner)
{ return Interp<0, Inner>(inner); }
class Field
{
public:
Field(const Grid& grid) :
grid_(grid),
data_(new double[grid_.icells]) {}
inline double operator()(const int i) const
{ return data_[i]; }
inline double& operator()(const int i)
{ return data_[i]; }
template<class T>
inline Field& operator+=(const T& expression)
{
for (int i=grid_.istart; i<grid_.iend; ++i)
(*this)(i) += expression(i);
return *this;
}
private:
const Grid& grid_;
double* data_;
};
int main()
{
Grid grid(256, 4);
Field u (grid);
Field ut(grid);
// Like this it crashes
auto expression = Ix_h( Ix(u) );
ut += expression;
// But like this it does not
ut += Ix_h( Ix(u) );
return 0;
}
auto expression = Ix_h( Ix(u) );
此处,Ix(u)
创建了一个绑定到转换构造函数 Interp<0, Interp<1, Field>>::Interp(Inner const&)
引用的临时对象。构造函数初始化对象的引用inner_
。您现在有一个临时值的引用,该值将在完整表达式 Ix_h( Ix(u) )
.
它在您执行 ut += Ix_h( Ix(u) )
时起作用的原因是因为引用和临时变量在表达式末尾消失。初始化 expression
只是传递引用。然后使用 ut += expression
使用一个已经死亡的对象,这是未定义的行为。
解决方案:使 inner_
成为对象而不是引用,以便发生复制:
Inner inner_;