3

我正在为计算内核构建带有表达式模板的代码。我的问题很简短:为什么 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;
}
4

1 回答 1

6
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只是简单地放弃引用。然后 usingut += expression使用一个已经死亡的对象,这是未定义的行为。

解决方案:inner_创建一个对象而不是引用,以便发生副本:

Inner inner_;
于 2015-02-05T23:11:11.553 回答