Reputation: 1526
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
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
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