0

I am working on a vector class that uses expression templates. I have the following (reduced) code. In "Vector.h":

#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits>

#define NULLVAL(a) (*((a*) 0))

namespace VectorExpression {
    template <typename E, typename R> class Expression;
    template <typename E, typename R, typename S, typename Op> class ScalarBinary;
    template <tyepname E1, typename R1, typename E2, typename R2, typename Op> class VectorBinary;
    template <typename T1, typename T2> struct Div;
    template <typename T1, typename T2> struct Div2;
    template <typename T> class Vector;
};

template <typename E, typename R, typename S>
typename std::enable_if<std::is_arithmetic<S>::value, VectorExpression::ScalarBinary<E, R, S, VectorExpression::Div<R, S>>>::type
operator/(const VectorExpression::Expression<E, R> &expr, S a)  // LINE #136
{
    return ( VectorExpression::ScalarBinary<E, R, S, VectorExpression::Div<R, S>>(expr, a) );
}

template <typename E, typename R, typename S>
typename std::enable_if<std::is_arithmetic<S>::value, VectorExpression::ScalarBinary<E, R, S, VectorExpression::Div2<R, S>>>::type
operator/(S a, const VectorExpression::Expression<E, R> &expr)  // LINE #143
{
    return ( VectorExpression::ScalarBinary<E, R, S, VectorExpression::Div2<R, S>>(expr, a) );
}

template <typename E1, typename R1, typename E2, typename R2>
auto operator/(const VectorExpression::Expression<E1, R1> &expr1, const VectorExpression::Expression<E2, R2> &expr2) -> VectorExpression::VectorBinary<E1, R1, E2, R2, VectorExpression::Div<R1, R2>>  // LINE #174
{
    return ( VectorExpression::VectorBinary<E1, R1, E2, R2, VectorExpression::Div<R1,R2>>(expr1, expr2) );
}

// length #1
template <typename E, typename R>
double length(const VectorExpression::Expression<E, R> &expr)
{
    return ( sqrt( pow(expr.x(), 2) + pow(expr.y(), 2) + pow(expr.z(), 2) );
}

// length #2
template <typename T>
double length(const VectorExpression::Expression<Vector<T>, T> &expr) // length #2
{
    return ( sqrt( expr.x() * expr.x() + expr.y() * expr.y() + expr.z() * expr.z() );
}

// normalize #1
template <typename E, typename R>
auto normalize(const VectorExpression::Expression<E, R> &expr) -> decltype( NULLVAL(Vector<double>) / NULLVAL(double) )
{
    Vector<double> v(expr);
    return ( v / v.length() );
}

// normalize #2
template <typename T>
auto normalize(const VectorExpression::Expression<Vector<T>, T> &expr) -> decltype( NULLVAL(Vector<double>) / NULLVAL(double) )
{
    return ( static_cast<Vector<double>>(expr) / expr.length() );
}

namespace VectorExpression {
    template <typename E, typename R>
    class Expression {
    protected:
        Expression() { }

    public:
        typedef R value_type;

        value_type x() const
        {
            return ( static_cast<const E&>(*this).x() );
        }
        value_type y() const
        {
            return ( static_cast<const E&>(*this).y() );
        }
        value_type z() const
        {
            return ( static_cast<const E&>(*this).z() );
        }


        double length() const
        {
            return ( ::length(static_cast<const E&>(*this)) );
        }
        auto normalize() -> decltype( NULLVAL(Vector<double>) / NULLVAL(double) )  // LINE #323
        {
            return ( ::normalize(static_cast<const E&>(*this)) );
        }
    };

    template <typename E, typename R, typename S, typename Op>
    class ScalarBinary : public Expression<ScalarBinary<E, R, S, Op>, typename Op::value_type> {
    private:
        const E& v;
        S a;

    public:
        ScalarBinary(const Expression<E, R> &expr, S scalar) : v(expr), a(scalar) { }

        value_type x() const
        {
            return ( Op::apply( v.x(), a ) );
        }
        value_type y() const
        {
            return ( Op::apply( v.y(), a ) );
        }
        value_type z() const
        {
            return ( Op::apply( v.z(), a ) );
        }
    };

    template <typename E1, typename R1, typename E2, typename R2, typename Op>
    class VectorBinary : public Expression<VectorBinary<E1, R1, E2, R2, Op>, typename Op::value_type> {
    private:
        const E1 &v1;
        const E2 &v2;

    public:
        VectorBinary(const Expression<E1, R1> &expr1, const Expression<E2, R2> &expr2) : v1(expr1), v2(expr2) { }

        value_type x() const
        {
            return ( Op::apply( v1.x(), v2.x() ) );
        }
        value_type y() const
        {
            return ( Op::apply( v1.y(), v2.y() ) );
        }
        value_type z() const
        {
            return ( Op::apply( v1.z(), v2.z() ) );
        }
    };

    template <typename T1, typename T2>
    struct Div {
        typedef decltype( NULLVAL(T1) / NULLVAL(T2) ) value_type;
        static value_type apply( T1 x1, T2 x2 ) { return ( x1 / x2 ); }
    };

    template <typename T1, typename T2>
    struct Div2 {
        typedef decltype( NULLVAL(T2) / NULLVAL(T1) ) value_type;
        static value_type apply( T1 x1, T2 x2 ) { return ( x2 / x1 ); }
    };

    template <typename T>
    class Vector : public Expression<Vector<T>, T> {  // LINE #578
    private:
        T _x, _y, _z;

    public:
        template <typename U1, typename U2, typename U3>
        Vector(U1 X, U2 Y, U3 Z) : _x(static_cast<T>(X)), _y(static_cast<T>(Y)), _z(static_cast<T>(Z)) { }


        template <typename E, typename R>
        Vector(const Expression<E, R> &expr) : _x(static_cast<T>(expr.x())), _y(static_cast<T>(expr.y())), _z(static_cast<T>(expr.z())) { }

        value_type x() const
        {
            return ( this->_x );
        }
        value_type y() const
        {
            return ( this->_y );
        }
        value_type z() const
        {
            return ( this->_z );
        }
    };
};

Then, in "main.cpp":

#include "Vector.h"

int main(int argc, char *argv[])
{
    Vector<double> a(0.5, 1.5, 2.5), b;  // LINE #12
    double c;

    c = length(a);  // calls length #1
    c = length(a/2.0);  // calls length #2
    a.length();  // calls length #1
    (a/2).length();  // calls length #2
    b = normalize(a);  // calls normalize #1
    b = normalize(a/2.0);  // calls normalize #2

    return ( 0 );
}

Now, if I comment out the member normalize in class Expression, the code compiles, and everything works as I expect, including which versions of length and normalize get called. However, if I leave the member normalize uncommented, I get the following compiler error:

vector.h(323): error C2784: 'VectorExpression::VectorBinary<E1,R1,E2,R2,VectorExpression::Div<R1,R2>> operator /(const VectorExpression::_Expression<E,R> &,const VectorExpression::_Expression<E2,R2> &)' : could not deduce template argument for 'const VectorExpression::_Expression<E,R> &' from 'VectorExpression::Vector<T>'
with
[
    T=double
]
vector.h(174) : see declaration of 'operator /'
vector.h(384) : see reference to class template instantiation 'VectorExpression::_Expression<E,R>' being compiled
with
[
    E=VectorExpression::Vector<double>,
    R=double
]
vector.h(578) : see reference to class template instantiation 'VectorExpression::Expression<E,R>' being compiled
with
[
    E=VectorExpression::Vector<double>,
    R=double
]
main.cpp(12) : see reference to class template instantiation 'VectorExpression::Vector<T>' being compiled
with
[
    T=double
]
vector.h(323): error C2784: 'std::enable_if<std::is_arithmetic<S>::value,VectorExpression::ScalarBinary<E,R,S,VectorExpression::Div2<R,S>>>::type operator /(S,const VectorExpression::_Expression<E,R> &)' : could not deduce template argument for 'const VectorExpression::_Expression<E,R> &' from 'double'
vector.h(143) : see declaration of 'operator /'
vector.h(323): error C2784: 'std::enable_if<std::is_arithmetic<S>::value,VectorExpression::ScalarBinary<E,R,S,VectorExpression::Div<R,S>>>::type operator /(const VectorExpression::_Expression<E,R> &,S)' : could not deduce template argument for 'const VectorExpression::_Expression<E,R> &' from 'VectorExpression::Vector<T>'
with
[
    T=double
]
vector.h(136) : see declaration of 'operator /'
vector.h(323): error C2676: binary '/' : 'VectorExpression::Vector<T>' does not define this operator or a conversion to a type acceptable to the predefined operator
with
[
    T=double
]

I would like to be able to call normalize as either a standalone function acting on a Vector or Expression or as an inherited member of Expression similar to the way it is done with length, which compiles and works correctly. The standalone versions of normalize also work correctly. What is wrong with my definition of normalize as a class member?

Thanks!

LRC
  • 1
  • 2
  • For me, that's too much code to go through to figure what the problem could be. – R Sahu Mar 03 '15 at 16:44
  • I understand. Unfortunately, that was all the further I was able to reduce it while keeping everything relevant. However, the error arises only when the definition of `auto normalize()` in the class `Expression` is uncommented, and that is only a few lines. Is there something obvious I am missing? – LRC Mar 03 '15 at 16:52

1 Answers1

0

It compiles and works correctly if I change

auto normalize() -> decltype( NULLVAL(Vector<double>) / NULLVAL(double) )

in the class Expression to

auto normalize() -> decltype( (*((Expression<Vector<double>,double>*) 0)) / NULLVAL(double) )

However, I do not understand why this is necessary. The standalone declarations of normalize use the former style of decltype and work correctly, and Vector<double> inherits from Expression<Vector<double>,double>, so I don't understand why I must make this change.

LRC
  • 1
  • 2