Marko Taht
Marko Taht

Reputation: 1526

C++ expression templates ambiguous operator overloading

Im trying to implement vector and matrix expression templates.Both have the operator+ overload but i get ambiguous operator error.

How can i overload operators for both matrix and vector while retaining the effects of expression templates?

Here is my matrix sum template:

    template<typename E1, typename E2>
class MatSum: public MatExpression<MatSum<E1,E2>>{
    E1 const& _u;
    E2 const& _v;

public:
    MatSum(E1 const &u, E2 const &v): _u(u), _v(v){
        assert(u.height() == v.height() && u.width() == v.width());
    }

    double operator[](size_t i) const {return _u[i] + _v[i]; }

    size_t size() const {return _u.size();}
    size_t width() const {return _u.width();}
    size_t height() const {return _u.height();}
};

template<typename E1, typename E2>
MatSum<E1,E2> operator+(E1 const& u, E2 const& v){
    return MatSum<E1,E2>(u,v);
}

and here is vector sum template:

template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2>>{
    E1 const& _u;
    E2 const& _v;

public:
    VecSum(E1 const & u, E2 const &v): _u(u), _v(v){
        assert(u.size() == v.size());
    }

    double operator[](size_t i) const {return _u[i] + _v[i]; }
    size_t size() const {return _v.size(); }
};


template <typename E1, typename E2>
VecSum<E1, E2> operator+(E1 const &u, E2 const &v){
    return VecSum<E1,E2>(u,v);
}

And the code piece that causes the error:

    Vec v1 = {67.12,34.8,90.34};

    Vec v2 = {34.90,111.9,45.12};

    Vec sum = v1+v2;

Matrix m0 = {{1.0,1.0,1.0},{1.0,1.0,1.0},{1.0,1.0,1.0}};
    Matrix m1 = {{1.0,1.0,1.0},{1.0,1.0,1.0},{1.0,1.0,1.0}};

    Matrix summ = m0 + m0;

Upvotes: 0

Views: 256

Answers (2)

Caleth
Caleth

Reputation: 62719

There's nothing to distinguish between your two templates, so it's an ambiguous call. You will have to restrict the one that returns MatSum to only apply to matrix-like arguments, and/or the one that returns VecSum to only apply to vector-like arguments.

Let's assume you add a member alias using kind = Mat; to MatExpression and Matrix, and using kind = Vec to VecExpression and Vector. We then make a traits class

template<typename E1, typename E2, typename = typename E1::kind, typename = typename E2::kind>
struct Traits;

template<typename E1, typename E2>
struct Traits<E1, E2, Mat, Mat>
{
    using sum_type = MatSum<E1, E2>;
    // others, e.g. using prod_type = MatProd<E1, E2>;
};

template<typename E1, typename E2>
struct Traits<E1, E2, Vec, Vec>
{
    using sum_type = VecSum<E1, E2>;
    // others, e.g. using prod_type = VecProd<E1, E2>;
};

And some alias templates, e.g.

template<typename E1, typename E2>
using sum_t = typename Traits<E1, E2>::sum_type;

We can then constrain our + overload to only exist when Traits exists.

template<typename E1, typename E2>
sum_t<E1, E2> operator+(E1 const& u, E2 const& v){
    return sum_t<E1, E2>(u, v);
}

Upvotes: 0

Oliort UA
Oliort UA

Reputation: 1647

Templates can not be specialized with return type.

In your case compiler can not choose which operator+ to call as both of them can be instantinated with any two types. Consider these example:

Vec v1 = {67.12,34.8,90.34};
Matrix m1 = {{1.0,1.0,1.0},{1.0,1.0,1.0},{1.0,1.0,1.0}};
auto sum = v1+m1;

Which of the two operators should be called here? It's ambiguous for compiler.

You can make two not template operator+, which have (Vec, Vec) and (Matrix, Matrix) arguments or use SFINAE to check template operator argument types for some condition and substitute needed operator+ specialization.

Upvotes: 3

Related Questions