I have a CRTP base class as follows:
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);}
};
Then the derived class is defined as
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);
}
};
with defining my own operators in mind, I also have the following AddOperator
which also inherits from base
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;
};
Then the operator+
overload between a Derived
type and a primitive type returns only a proxy/expression of sorts:
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);
}
However, when I call this under clang
I get garbage values for rhs
of AddOperator
. Here is an example:
int main() {
Derived<double,2,2> g;
Derived<double,2,2> x = g+28;
return 0;
}
The other overloads when both lhs
and rhs
in AddOperator
are of type Derived
do not have this problem.
This problem happens only under clang
. gcc
compiled code seems to run fine. Does anyone know where the problem is?
Your problem is that you have a dangling reference.
In operator+
, you take a TRhs
(int
) by value and then construct an AddOperator<...>
with a reference to it. When g+28
returns, the AddOperator<...>
object still has a reference to the parameter rhs
- whose lifetime has now ended.
The garbage you're printing is the outcome of accessing a destroyed object - it's undefined behavior. On clang, this manifests itself as printing a garbage value for you, on gcc it happens to work. Undefined behavior is tricky like that.
Now, the seemingly "obvious" fix is to change operator+
to take rhs
by reference to const
. This will extend the lifetime of the temporary 28
through the end of the full expression containing the call - so now your print statements are guaranteed to work. At least, until the end of the line. After x
is constructed, the reference will dangle yet again.