mercredi 24 février 2016

How should I use expression templates in order to implement scalar multiplication for a mathematical vector class

Please consider the following (partial) implementation of a mathematical vector class (which is basically the code you can find in the Wikipedia article about expression templates):

namespace math
{
    template<class E>
    class vector_expression
    {
    public:
        std::size_t size() const {
            return static_cast<E const&>(*this).size();
        }

        double operator[](size_t i) const
        {
            if (i >= size())
                throw std::length_error("");
            return static_cast<E const&>(*this)[i];
        }

        operator E&() { return static_cast<E&>(*this); }
        operator E const&() const { return static_cast<E const&>(*this); }
    }; // class vector_expression

    template<class E1, class E2>
    class vector_sum
        : public vector_expression<vector_sum<E1, E2>>
    {
    public:
        vector_sum(vector_expression<E1> const& e1, vector_expression<E2> const& e2)
            : m_e1(e1), m_e2(e2)
        {
            if (e1.size() != e2.size())
                throw std::logic_error("");
        }

        std::size_t size() const {
            return m_e1.size(); // == m_e2.size()
        }

        double operator[](std::size_t i) const {
            return m_e1[i] + m_e2[i];
        }

    private:
        E1 const& m_e1;
        E2 const& m_e2;
    }; // class vector_sum

    template<typename E1, typename E2>
    vector_sum<E1, E2> operator+(vector_expression<E1> const& e1, vector_expression<E2> const& e2) {
        return { e1, e2 };
    }

    template<typename T>
    class vector
        : public vector_expression<vector<T>>
    {
    public:
        vector(std::size_t d)
            : m_data(d)
        { }
        vector(std::initializer_list<T> init)
            : m_data(init)
        { }
        template<class E>
        vector(vector_expression<E> const& expression)
            : m_data(expression.size())
        {
            for (std::size_t i = 0; i < expression.size(); ++i)
                m_data[i] = expression[i];
        }

        std::size_t size() const {
            return m_data.size();
        }

        double  operator[](size_t i) const { return m_data[i]; }
        double& operator[](size_t i) { return m_data[i]; }

    private:
        std::vector<T> m_data;
    }; // class vector    
} // namespace math

How should I extend this implementation to allow the following operations:

vector<double> x = { ... };
auto y = 4711 * x; // or y = x * 4711
auto z = 1 + x; // or x + 1, which should yield z[i] = x[i] + 1

I suppose that I need something like

namespace math
{
    template<class E, typename T>
    class vector_product
        : public vector_expression<vector_product<E, T>>
    {
    public:
        vector_product(vector_expression<E> const& e, T const& t)
            : m_e(e), m_t(t)
        { }

        std::size_t size() const {
            return m_e.size();
        }

        double operator[](std::size_t i) const {
            return m_e[i] * m_t;
        }

    private:
        E const& m_e;
        T const& m_t;
    }; // class vector_product

    template<class E, typename T>
    vector_product<E, T> operator*(vector_expression<E> const& e, T const& t) {
        return { e, t };
    }
    template<class E, typename T>
    vector_product<E, T> operator*(T const& t, vector_expression<E> const& e) {
        return e * t;
    }   
} // namespace math

but I don't know whether or not this is a good approach. So, how should I do it? And should I add any copy or move constructor/assignment operator? I guess not, since the implicit ones should do a perfect job, cause the only member variable of vector is a STL type.

Aucun commentaire:

Enregistrer un commentaire