2012-03-16 23 views
1

我用下面的代碼編譯器相關的問題發生(存儲在crtp.cc):CRTP:用表達式模板編譯器相關的問題

#include <vector> 
#include <cassert> 
#include <iostream> 

template < class Derived > 
class AlgebraicVectorExpression { 
public: 
    typedef std::vector<double>::size_type SizeType; 
    typedef std::vector<double>::value_type ValueType; 
    typedef std::vector<double>::reference ReferenceType; 

    SizeType size() const { 
    return static_cast<const Derived&>(*this).size(); 
    } 

    ValueType operator[](SizeType ii) const { 
    return static_cast<const Derived&>(*this)[ii]; 
    } 

    operator Derived&() { 
    return static_cast<Derived&>(*this); 
    } 

    operator const Derived&() const { 
    return static_cast< const Derived& >(*this); 
    } 
}; 

template< class T1, class T2> 
class AlgebraicVectorSum : public AlgebraicVectorExpression< AlgebraicVectorSum<T1,T2> > { 
    const T1 & a_; 
    const T2 & b_; 

    typedef typename AlgebraicVectorExpression< AlgebraicVectorSum<T1,T2> >::SizeType SizeType; 
    typedef typename AlgebraicVectorExpression< AlgebraicVectorSum<T1,T2> >::ValueType ValueType; 
public: 

    AlgebraicVectorSum(const AlgebraicVectorExpression<T1>& a, const AlgebraicVectorExpression<T1>& b) : 
    a_(a), b_(b) { 
    assert(a_.size() == b_.size()); 
    } 

    SizeType size() const { 
    return a_.size(); 
    } 

    ValueType operator[](SizeType ii) const { 
    return (a_[ii] + b_[ii]); 
    } 

}; 

template< class T1, class T2> 
const AlgebraicVectorSum<T1,T2> 
operator+(const AlgebraicVectorExpression<T1>& a, const AlgebraicVectorExpression<T2>& b) { 
    return AlgebraicVectorSum<T1,T2>(a,b); 
} 

class AlgebraicVector : public AlgebraicVectorExpression<AlgebraicVector>{ 
    std::vector<double> data_; 

public: 
    SizeType size() const { 
    return data_.size(); 
    } 

    ValueType operator[](SizeType ii) const { 
    return data_[ii]; 
    } 

    ValueType& operator[](SizeType ii) { 
    return data_[ii]; 
    } 

    AlgebraicVector(SizeType n) : data_(n,0.0) { 
    }; 

    template< class T> 
    AlgebraicVector(const AlgebraicVectorExpression<T>& vec) { 
    const T& v = vec; 
    data_.resize(v.size()); 
    for(SizeType idx = 0; idx != v.size(); ++idx) { 
     data_[idx] = v[idx]; 
    } 
    } 
}; 

int main() { 

    AlgebraicVector x(10); 
    AlgebraicVector y(10); 
    for (int ii = 0; ii != 10; ++ii) 
    x[ii] = y[ii] = ii;  

    AlgebraicVector z(10); 
    z = x + y; 

    for(int ii = 0; ii != 10; ++ii) 
    std::cout << z[ii] << std::endl; 
    return 0; 
} 

事實上,當我編譯:

$ g++ --version 
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 
Copyright (C) 2009 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

$ g++ -O0 -g crtp.cc 

我獲得:

$ ./a.out 
0 
2 
4 
6 
8 
10 
12 
14 
16 
18 

這是預期的行爲。當我使用ICPC:

$ icpc --version 
icpc (ICC) 12.1.0 20110811 
Copyright (C) 1985-2011 Intel Corporation. All rights reserved.  
$ icpc -g -O0 crtp.cc 

我獲得,而不是Segmentation fault。運行

valgrind --tool=memcheck ./a.out 

點在源線29

AlgebraicVectorExpression<AlgebraicVector>::operator AlgebraicVector const&() const (crtp.cc:29) 

由於我很新的C++和我花了相當的時間,沒有任何結果,尋找一個錯誤,我想問的意見有更有經驗的人瞭解這個問題是由於我引入的錯誤(如我所期望的)還是編譯器錯誤。

編輯: 我改變了現在的代碼,在Mike Seymour的回答之後。現在我沒有得到編譯器警告,但我仍然得到了和以前一樣的行爲(使用相同的valgrind響應)。有沒有人試圖與英特爾編譯?

編輯: 我試圖編譯維基百科頁面Expression Templates中的代碼。我獲得了與我提供的示例相同的確切行爲。

編輯: 我已經進一步研究這個問題,似乎與英特爾icpc操作

operator const Derived&() const { 
    return static_cast< const Derived& >(*this); 
    } 

遞歸調用自身編譯。一個解決方法,我發現是一個方法來替代這個操作符:

const Derived& get_ref() const { 
    return static_cast< const Derived& >(*this); 
    } 

並相應地修改的各種類的構造函數。任何人都可以告訴這兩種行爲中的哪一種可能指向標準來解釋這一點嗎?

回答

6

您應該始終啓用編譯器警告;他們往往可以發現微妙的問題。在這種情況下:

g++ -Wall -Wextra test.cpp 
test.cpp: In member function ‘const typename AlgebraicVectorExpression<AlgebraicVectorSum<T1, T2> >::ValueType& AlgebraicVectorSum<T1, T2>::operator[](typename AlgebraicVectorExpression<AlgebraicVectorSum<T1, T2> >::SizeType) const [with T1 = AlgebraicVector, T2 = AlgebraicVector]’: 
test.cpp:90: instantiated from ‘AlgebraicVector::AlgebraicVector(const AlgebraicVectorExpression<T1>&) [with T = AlgebraicVectorSum<AlgebraicVector, AlgebraicVector>]’ 
test.cpp:103: instantiated from here 
test.cpp:52: warning: returning reference to temporary 

這告訴你這個問題:

const ValueType& operator[](SizeType ii) const { 
    return (a_[ii] + b_[ii]); 
} 

表達式的結果是暫時的,在該行的末尾破壞,因此函數返回一個懸空引用一個不存在的對象。此運算符將不得不按值返回,並且不應執行非const過載,因爲沒有要修改的值。

+0

+1剛剛發現這一點。 – hmjd 2012-03-16 14:34:15

+0

非const的操作符[]'也遭受同樣的問題,不能實例化,因爲我沒有收到任何警告。 – hmjd 2012-03-16 14:35:47

+0

@hmjd:無論如何,你無法明智地實現'AlgebraicVectorSum'的非const構造。 const版本應該只返回一個值而不是引用,它會很好。我也很好奇你在這種情況下通過擁有'AlgebraicVectorExpression'並使用CRTP而獲得什麼。 – Omnifarious 2012-03-16 14:39:09