0

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;
Marko Taht
  • 1,448
  • 1
  • 20
  • 40
  • 1
    Have you tried to define operator+ as member method of your class ? – Gojita Apr 02 '19 at 13:56
  • 2
    If you only have one type of `Vec` and `Matrix` you can make the operator take parameters of that type instead of using templates. Otherwise you can do the same thing using SFINAE, make the `VecSum` operator take only `Vec<...>` parameters. – super Apr 02 '19 at 14:05
  • `MatSum::operator[]` returns `double` ? – Jarod42 Apr 02 '19 at 15:07
  • @Gojita - operator overloading is more of a syntactic sugar for the template constructors. The whole point is to minimize the assembly code that will be generated when program is compiled. I think putting the operator as member of the class will contradict this idea. – Marko Taht Apr 03 '19 at 06:22
  • @super - I tryed using SFINAE but failed, SFINAE is kinda difficult to understand how to use it. – Marko Taht Apr 03 '19 at 06:23
  • @Jarod42 - Yes. The whole setup constructs a expression tree that will then collapse into more concise piece of code that will create the new matrix without creating any intermediary temp variables. Its the point of Expression templates. Matrix class has a constructor that accepts MatExpression as parameter, and only there will the equation be evaluated, no where else. – Marko Taht Apr 03 '19 at 06:23
  • I meant that I expect `MatSum::operator[]` to returns some kind of `VecView`, or taking `std::pair`. – Jarod42 Apr 03 '19 at 07:48

2 Answers2

3

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.

Oliort UA
  • 1,568
  • 1
  • 14
  • 31
0

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);
}
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • 2
    alias is from C++11, You might even directly do `template struct Traits` – Jarod42 Apr 02 '19 at 15:25
  • @Jarod42 I get confused on the history because all the `using foo_t = typename foo<>::type` aliases in `std` are C++14 – Caleth Apr 02 '19 at 15:30
  • I based my expression templates on this https://en.wikipedia.org/wiki/Expression_templates sins Vec is a subclass of VecExpression. Then it should suffice if the using kind = Vec only for VecExpression. correct? – Marko Taht Apr 03 '19 at 06:34
  • @Caleth im trying to add this Traits class. But i fail atthe point of using kind = Mat; and using kind = Vec;. Do i have to create some empty classes for Mat and Vec and then i can use this? Or how do you do that? – Marko Taht Apr 03 '19 at 07:03
  • @MarkoTaht It can be any two distinct types – Caleth Apr 03 '19 at 07:13
  • @Caleth but where should the using kind = Mat; be? How would one actually use it? And can i place this traits stuff in a separate header file or should it all be in one place? – Marko Taht Apr 03 '19 at 07:24
  • @MarkoTaht as a member of `Matrix` and also as a member of `MatExpression` – Caleth Apr 03 '19 at 07:24
  • @Caleth traits.h|7|error: need 'typename' before 'E1::kind' because 'E1' is a dependent scope| – Marko Taht Apr 03 '19 at 07:29
  • @MarkoTaht: So add `typename` as compiler tells you :-) (`typename E1::kind`). – Jarod42 Apr 03 '19 at 07:50
  • template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)| – Marko Taht Apr 03 '19 at 08:08
  • @MarkoTaht I'm not sure what template you are recursively instantiating there – Caleth Apr 03 '19 at 08:14
  • @Caleth this error seems to be cause by "template using sum_t = typename Traits::sum_type;" – Marko Taht Apr 03 '19 at 08:17
  • @MarkoTaht yes, but there's no loop of definitions up to `MatExpression>` in the code shown. If you remove all the vector code, and the traits, does `MatExpression>` still have infinite descent of template expansion? – Caleth Apr 03 '19 at 08:19
  • @Caleh I placed the full code i currently have here https://codeshare.io/5wxbQj And pure Matrix code works just fine. – Marko Taht Apr 03 '19 at 08:22
  • @Caleth Pure Matrix ET or pure Vector ET works just fine. Its the step where i want both of them in the same program i start getting the abiguiti problem. And the infinte decent comes when i add in the changes to doe the sum_t thing. – Marko Taht Apr 03 '19 at 08:56
  • Aah... found the issue... problem was that using kind was set as private :D – Marko Taht Apr 03 '19 at 10:02