c++c++11expression-templates

Expression templates not working for primitive type overloads under clang


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?

Full Demo Here


Solution

  • 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.