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