3

計算カーネルの式テンプレートを使用してコードを作成しています。私の質問は非常に短いです: なぜ GNU G++ は+=、次の例のを含む行で segfault (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 に答える