Handling references to `expressions` in a `vector_binary_operation` class without unnecessary copies
<p>I'm trying to implement a simple <code>vector</code> class which uses <a href="https://en.wikipedia.org/wiki/Expression_templates" rel="nofollow noreferrer">expression templates</a> in order to avoid that expressions such as <code>vector w = x + y + z</code> are implemented inefficiently (the implementation would first produce a temporary vector to hold <code>x + y</code> and then produce another <code>vector</code> with the elements of <code>z</code> added in):</p>
<pre><code>namespace math
{
    template&lt;class E&gt;
    class expression
    {
    public:
        auto size() const {
            return static_cast&lt;E const&amp;&gt;(*this).size();
        }

        auto operator[](std::size_t i) const
        {
            if (i &gt;= size())
                throw std::length_error(&quot;&quot;);
            return static_cast&lt;E const&amp;&gt;(*this)[i];
        }

        operator E&amp;() { return static_cast&lt;E&amp;&gt;(*this); }
        operator E const&amp;() const { return static_cast&lt;E const&amp;&gt;(*this); }
    }; // class expression

    template&lt;typename T, class Allocator = std::allocator&lt;T&gt;&gt;
    class vector
        : public expression&lt;vector&lt;T&gt;&gt;
    {
    private:
        using data_type = std::vector&lt;T, Allocator&gt;;
        data_type m_data;

    public:
        using value_type = T;
        using allocator_type = Allocator;
        using size_type = typename data_type::size_type;
        using difference_type = typename data_type::difference_type;
        using reference = typename data_type::reference;
        using const_reference = typename data_type::const_reference;
        using pointer = typename data_type::pointer ;
        using const_pointer = typename data_type::const_pointer;

        vector(size_type d)
            : m_data(d)
        { }
        vector(std::initializer_list&lt;value_type&gt; init)
            : m_data(init)
        { }
        template&lt;class E&gt;
        vector(expression&lt;E&gt; const&amp; expression)
            : m_data(expression.size())
        {
            for (size_type i = 0; i &lt; expression.size(); ++i)
                m_data[i] = expression[i];
        }

        size_type size() const {
            return m_data.size();
        }
        
        value_type  operator[](size_type i) const { return m_data[i]; }
        value_type&amp; operator[](size_type i)       { return m_data[i]; };
    }; // class vector

    namespace detail
    {
        template&lt;typename T&gt;
        class scalar
            : public expression&lt;scalar&lt;T&gt;&gt;
        {
        public:
            using value_type = T;
            using allocator_type = std::allocator&lt;void&gt;;
            using size_type = typename std::allocator&lt;T&gt;::size_type;
            using difference_type = typename std::allocator&lt;T&gt;::difference_type;
            using reference = typename std::allocator&lt;T&gt;::reference;
            using const_reference = typename std::allocator&lt;T&gt;::const_reference;
            using pointer = typename std::allocator&lt;T&gt;::pointer;
            using const_pointer = typename std::allocator&lt;T&gt;::const_pointer;

            scalar(value_type value)
                : m_value(value)
            { }

            size_type size() const {
                return 0;
            }

            operator value_type&amp;() { return static_cast&lt;value_type&amp;&gt;(*this); }
            operator value_type const&amp;() const { return static_cast&lt;value_type const&amp;&gt;(*this); }

            value_type  operator[](size_type i) const { return m_value; }
            value_type&amp; operator[](size_type i) { return m_value; }

        private:
            value_type m_value;
        }; // class scalar

        template&lt;class&gt;
        struct is_scalar : std::false_type { };

        template&lt;class T&gt;
        struct is_scalar&lt;scalar&lt;T&gt;&gt; : std::true_type { };
    } // namespace detail

    template&lt;class E1, class E2, class BinaryOperation&gt;
    class vector_binary_operation
        : public expression&lt;vector_binary_operation&lt;E1, E2, BinaryOperation&gt;&gt;
    {
    public:
        using value_type = decltype(BinaryOperation()(typename E1::value_type(), typename E2::value_type()));
        using allocator_type = std::conditional_t&lt;
            detail::is_scalar&lt;E1&gt;::value,
            typename E2::allocator_type::template rebind&lt;value_type&gt;::other,
            typename E1::allocator_type::template rebind&lt;value_type&gt;::other&gt;;

    private:
        using vector_type = vector&lt;value_type, allocator_type&gt;;

    public:
        using size_type = typename vector_type::size_type;
        using difference_type = typename vector_type::difference_type;
        using reference = typename vector_type::reference;
        using const_reference = typename vector_type::const_reference;
        using pointer = typename vector_type::pointer;
        using const_pointer = typename vector_type::const_pointer;

        vector_binary_operation(expression&lt;E1&gt; const&amp; e1, expression&lt;E2&gt; const&amp; e2, BinaryOperation op)
            : m_e1(e1), m_e2(e2),
              m_op(op)
        {
            if (e1.size() &gt; 0 &amp;&amp; e2.size() &gt; 0 &amp;&amp; !(e1.size() == e2.size()))
                throw std::logic_error(&quot;&quot;);
        }

        size_type size() const {
            return m_e1.size(); // == m_e2.size()
        }

        value_type operator[](size_type i) const {
            return m_op(m_e1[i], m_e2[i]);
        }

    private:
        E1 m_e1;
        E2 m_e2;
        //E1 const&amp; m_e1;
        //E2 const&amp; m_e2;
        BinaryOperation m_op;
    }; // class vector_binary_operation

    template&lt;class E1, class E2&gt;
    vector_binary_operation&lt;E1, E2, std::plus&lt;&gt;&gt;
    operator+(expression&lt;E1&gt; const&amp; e1, expression&lt;E2&gt; const&amp; e2) {
        return{ e1, e2, std::plus&lt;&gt;() };
    }
    template&lt;class E1, class E2&gt;
    vector_binary_operation&lt;E1, E2, std::minus&lt;&gt;&gt;
    operator-(expression&lt;E1&gt; const&amp; e1, expression&lt;E2&gt; const&amp; e2) {
        return{ e1, e2, std::minus&lt;&gt;() };
    }
    template&lt;class E1, class E2&gt;
    vector_binary_operation&lt;E1, E2, std::multiplies&lt;&gt;&gt;
    operator*(expression&lt;E1&gt; const&amp; e1, expression&lt;E2&gt; const&amp; e2) {
        return{ e1, e2, std::multiplies&lt;&gt;() };
    }
    template&lt;class E1, class E2&gt;
    vector_binary_operation&lt;E1, E2, std::divides&lt;&gt;&gt;
    operator/(expression&lt;E1&gt; const&amp; e1, expression&lt;E2&gt; const&amp; e2) {
        return{ e1, e2, std::divides&lt;&gt;() };
    }

    template&lt;class E, typename T&gt;
    vector_binary_operation&lt;E, detail::scalar&lt;T&gt;, std::divides&lt;&gt;&gt;
    operator/(expression&lt;E&gt; const&amp; expr, T val) {
        return{ expr, detail::scalar&lt;T&gt;(val), std::divides&lt;&gt;() };
    }
    template&lt;class E, typename T&gt;
    vector_binary_operation&lt;E, detail::scalar&lt;T&gt;, std::multiplies&lt;&gt;&gt;
    operator*(T val, expression&lt;E&gt; const&amp; expr) {
        return{ expr, detail::scalar&lt;T&gt;(val), std::multiplies&lt;&gt;() };
    }
    template&lt;class E, typename T&gt;
    vector_binary_operation&lt;E, detail::scalar&lt;T&gt;, std::multiplies&lt;&gt;&gt;
    operator*(expression&lt;E&gt; const&amp; expr, T val) {
        return{ expr, detail::scalar&lt;T&gt;(val), std::multiplies&lt;&gt;() };
    }
} // namespace math
</code></pre>
<p>I don't know how I need to deal with the <code>expression</code> member variables in <code>vector_binary_operation</code>. It would make sense to declare them as const references, cause the whole point of the code is to avoid unnecessary copies. However, if we write <code>sum = a + b + c</code>, we will end up keeping a reference to the temporary <code>a + b</code>. Doing <code>sum[0]</code> will call <code>operator()[0]</code> on that temporary. But that object was deleted after the previous line.</p>
<p>What should I do?</p>