-1

I have a template struct "point as follows:

template<typename T>
struct point
{
    T x, y, z;


    template<typename T1>
    inline point<T> operator*(const point<T1>& p) const // multiply by another point.
    {   
        return point<T>{this->x*p.x, this->y*p.y, this->z*p.z};
    }

    template<typename T1>
    inline point<T> operator*(const T1& v) const // multiply by constant from right side
    {
        return point<T>{this->x*v, this->y*v, this->z*v};
    }
}


template<typename T1, typename T2>
inline point<T1> operator*(const T2& v, const point<T1>& p) // multiply by a constant from the left side.
{
    return point<T1>{p.x*v, p.y*v, p.z*v};
} 

The two operator overloading functions that declared as member functions, the first one is supposed to multiply by another point, and the other to multiply a point by a constant from the right side, where the one declared outside the struct is to do the same thing but from the left side.

And now when I go to compile the following code:

point<double> p1{1,2,3};
point<float> p2{1,2,3}; 

point<double> p3 = p1*p3;

The compiler calls the one declared outside the struct instead of the one declared as a member function, and produces and error:

error: cannot convert ‘point<double>’ to ‘float’ in initialization
             return point<T1>{p.x*v, p.y*v, p.z*v};

And that makes sense, because the both arguments are template variables and can be interpreted as point variables. Now the second one can be only a point variable, but the first one can be anything!

To solve this I can go and write several copies of that function, when declaring the first argument once as int, float, double, long double .. et cetera. This works fine for me, but I'm still wondering if there is a better way to deal with this, and if I can write only one copy as above?

Bobo Feugo
  • 162
  • 1
  • 10
  • The compiler isn't calling "the one declared outside the struct". It is failing to compile due to a missing `;` after the `struct` definition, before it even reaches the code that uses the `point` template. Fix that, and the code still won't compile, since the presence of multiple `operator*` makes the expression `p1 *p3` ambiguous (multiple `operator*()` are equally good candidates). Also, it is not a good idea to initialise a variable in terms of itself (as in `point p3 = p1*p3;`. The problem you asked about is essentially a typo (missing `;`). Voting to close accordingly. – Peter Feb 23 '19 at 13:42
  • Nop! The issue has nothing to do with semicolon ; , the code I wrote above was just a draft, and it wasn't able to compile because the compiler interpreted the both arguments of the non-member function as point instances, where my intent was the first to be a constant and the second to be a point instance! – Bobo Feugo Feb 23 '19 at 15:20
  • I just received an error message similar to your description, with the semi-colon absent. And an error about ambiguity when the semi-colon was added. People can only provide advice based on information you provide. If the "draft" doesn't represent your problem, the fault is yours. Read up on the need to provide a [mcve]. – Peter Feb 23 '19 at 22:59

1 Answers1

1

Do not unnecessarily overload operators as member functions, follow the rules described here.

Having operator* for two point instances return the type of the left one does not make sense, in my opinion. Better consider both types in the deduction of the return type, since the function is defined as commutative.

template<typename T>
struct point
{
    T x, y;
};

template<typename T1, typename T2>
auto operator*(const point<T1>& lhs, const point<T2>& rhs)
    -> point<decltype(std::declval<T1>() * std::declval<T2>())>
{   
    return {lhs.x*rhs.x, lhs.y*rhs.y};
}

template<typename T1, typename T2>
point<T1> operator*(const point<T1>& p, const T2& v)
{
    return {p.x*v, p.y*v};
}

template<typename T1, typename T2>
point<T1> operator*(const T2& v, const point<T1>& p)
{
    return p * v;
}

Try to do the same with the overloads for multiplication by a scalar as an exercise.

LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • As I can see the problem was a matter of declare all of them as non-member function. So what was the purpose of that extension you wrote with the first function ( -> point() * std::declval())> ), cause for me it worked without those?! – Bobo Feugo Feb 23 '19 at 15:19
  • @BoboFeugo I explained that in the answer. You get `point` if you do `point() * point()` and `point` if you do `point() * point()`. Should it "work" that way? I don't think so. Perhaps you could only allow multiplication of `point`s of the same type. – LogicStuff Feb 23 '19 at 16:25
  • 1
    Got it! actually the decision on the return type is a point I haven't thought of, as I was trying to always return the type of the first argument. But it was a good advice to point to that :-) – Bobo Feugo Feb 23 '19 at 16:51