Шаблоны выражений не работают для перегрузок примитивного типа при clang

У меня есть базовый класс 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 type и примитивный тип возвращает только прокси / выражение вида:

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 Скомпилированный код, кажется, работает нормально. Кто-нибудь знает, где проблема?

Полная демонстрация здесь

1 ответ

Решение

Ваша проблема в том, что у вас есть свисающая ссылка.

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

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


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

Другие вопросы по тегам