2016-05-25 13 views
4

Ich habe eine CRTP Basisklasse wie folgt:Expression Templates arbeiten, nicht für primitive Art Überlastungen unter Klirren

template<typename Derived, size_t DIMS> 
class Base { 
public: 
    // here is I think where the problem is 
    inline const Derived& self() const {return *static_cast<const Derived*>(this);} 
}; 

Dann wird die abgeleitete Klasse ist definiert als

template<typename T, size_t ... Rest> 
class Derived: public Base<Derived<T,Rest...>,sizeof...(Rest)> { 
public: 

    Derived() = default; 

    // This constructor binds any arbitrary expression to Derived 
    template<typename Expr, size_t DIMS> 
    inline Derived(const Base<Expr,DIMS>& src_) { 
     const Expr &src = src_.self(); 
     print(src.rhs); 
    } 
}; 

mit meinen eigenen Betreiber im Sinn der Definition Ich habe auch die folgenden AddOperator, die auch von der Basis

template<typename TLhs, typename TRhs, size_t DIMS> 
struct AddOperator: public Base<AddOperator<TLhs, TRhs, DIMS>,DIMS> { 
    AddOperator(const TLhs& lhs, const TRhs& rhs) : lhs(lhs), rhs(rhs) { 
     print(rhs); 
    } 
    const TLhs &lhs; 
    const TRhs &rhs; 
}; 
erbt

Dann wird die operator+ Überlastung zwischen einem Derived Typ und einem primitiven Typ gibt nur einen Proxy/Ausdruck der Art:

template<typename TLhs, typename TRhs, size_t DIM0, 
     typename std::enable_if<!std::is_arithmetic<TLhs>::value && 
           std::is_arithmetic<TRhs>::value,bool>::type = 0 > 
inline AddOperator<TLhs, TRhs, DIM0> 
operator+(const Base<TLhs,DIM0> &lhs, TRhs rhs) { 
    return AddOperator<TLhs, TRhs, DIM0>(lhs.self(), rhs); 
} 

Allerdings, wenn ich das ich Müll Werte unter clang rufen erhalten für rhs von AddOperator. Hier ein Beispiel:

int main() { 

    Derived<double,2,2> g; 
    Derived<double,2,2> x = g+28; 

    return 0; 
} 

Die anderen Überlastungen, wenn beide lhs und rhs in AddOperator vom Typ sind Derived dieses Problem nicht hat. Dieses Problem tritt nur unter clang auf. gcc kompilierter Code scheint gut zu laufen. Weiß jemand, wo das Problem liegt?

Full Demo Here

Antwort

6

Ihr Problem ist, dass Sie eine baumelnde Referenz.

In operator+, nehmen Sie eine TRhs (int) nach Wert und dann einen AddOperator<...> mit einem Verweis auf sie bauen. Wenn g+28 zurückkommt, hat das Objekt AddOperator<...> immer noch einen Verweis auf den Parameter rhs - dessen Lebensdauer jetzt abgelaufen ist.

Der Papierkorb, den Sie drucken, ist das Ergebnis des Zugriffs auf ein zerstörtes Objekt - es ist ein undefiniertes Verhalten. Beim Klirren manifestiert sich dies als Ausdruck eines Müllwertes für Sie, bei gcc funktioniert es. Undefiniertes Verhalten ist so schwierig.


Nun, die scheinbar "offensichtliche" fix ist operator+ zu ändern rhs unter Bezugnahme auf const zu nehmen. Dadurch wird die Lebensdauer des temporären 28 bis zum Ende des vollständigen Ausdrucks, der den Aufruf enthält, verlängert. Jetzt funktionieren Ihre Druckanweisungen garantiert. Zumindest bis zum Ende der Linie. Nachdem x konstruiert ist, wird die Referenz noch einmal baumeln.

+0

Ich habe es selbst bemerkt, sobald ich die Frage gepostet habe. Vielen Dank –