2016-05-25 8 views
4

У меня есть CRTP базовый класс а следующим образом:шаблоны выражений не работает перегрузкам примитивных типов под звоном

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);} 
}; 

Тогда производный класс определяется как

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); 
    } 
}; 

с определением моих собственных операторов в виду , У меня также есть следующее AddOperator, которое также наследует от основания

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; 
}; 

Тогда operator+ перегрузки между типом Derived и примитивным типом возвращает только прокси/выражение своего рода:

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); 
} 

Однако, когда я называю это под clang я получаю значение мусора для rhs из AddOperator. Вот пример:

int main() { 

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

    return 0; 
} 

Другие перегрузках, когда оба lhs и rhs в AddOperator имеют тип Derived не имеют этой проблемы.

Эта проблема возникает только при clang. gcc скомпилированный код, похоже, работает нормально. Кто-нибудь знает, где проблема?

Full Demo Here

ответ

6

Ваша проблема в том, что у вас есть оборванных ссылок.

В operator+, вы берете TRhs (int) по значению, а затем построить AddOperator<...> со ссылкой на него. Когда возвращается g+28, объект AddOperator<...> все еще имеет ссылку на параметр rhs, срок службы которого закончился.

Мусор, который вы печатаете, является результатом доступа к разрушенному объекту - это неопределенное поведение. На clang это проявляется в том, что вы печатаете для себя мусорную ценность, на gcc это происходит. Неопределенное поведение сложно.


Теперь, казалось бы, «очевидно» исправление для изменения operator+ принять rhs ссылкой на const. Это продлит время жизни временного 28 до конца полного выражения, содержащего вызов, поэтому теперь ваши отчеты о печати гарантированно будут работать. По крайней мере, до конца строки. После того, как будет построено x, ссылка снова свисает.

+0

Я сам это заметил, как только отправил вопрос. благодаря –