使用通过 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_;