2015-02-05 2 views
3

전산 커널 용 표현 템플릿으로 코드를 생성 중입니다. 내 질문은 매우 짧 :자동 키워드로 작성된 유형을 사용하는 표현식 템플릿의 세그먼트 오류

// Like this it crashes 
auto expression = Ix_h(Ix(u)); 
ut += expression; 

하지만 동등한 코드를 입력하지 : GNU G 않는 이유 ++ 다음 예에서 +=를 포함하는 라인 (-03 컴파일 4.9.1) 세그먼트 폴트를 제공합니다 :

// 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; 
} 
+0

을 당신은 아마 정의되지 않은 동작 시나리오로 실행 중입니다. Gdb가 최선의 방법이 될 것입니다. – James

답변

5
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_; 
+0

기본 객체 ('Field' 클래스)가 단순하면 실용적인 방법으로 만 작동 할 것입니다. – Chiel

+0

@Chiel 사본이 과도하고 비싸면 이동 의미론을 고려하십시오. 'Interp'가 rvalue-reference에 의해 인수를 취하고': inner_ (std :: move (inner))'를 수행하여'inner_'를 초기화하십시오. 이것은'Inner'가 move-semantics를 구현할 것을 요구합니다. 그렇지 않으면 괜찮아. 프로그램에서 병목 현상을 발견하고 발견 할 때까지 성능에 대해 걱정할 필요가 없습니다. – 0x499602D2

+0

이동 의미론을 사용하는 경우 오버로드 된 복합 할당이 모든 operator() 호출을 확장하면 전체 트리를 추적하는 방법은 무엇입니까? – Chiel