4

I'm trying to make a universal vector class, both for the sake of my own snippet library and to practice with templated classes. Essentially, the Vector class is templated to allow you to chose whether its precision is float, double, long double, etc.

The issue I've run into is with overloading the * operator for the purpose of scaling the vector. With all working overloads and member functions excluded, the class definition looks like this:

#pragma once

#include <math.h>   //  for sqrt function when normalizing

template <typename T> class Vector;
template <typename T> Vector<T> operator*(const Vector<T>& obj);

template <typename T> class Vector {
private:
    //  Attributes:
    static const int DIMS = 3;
    T component[DIMS];

public:
    enum { 
        X, Y, Z
    };

public:
    //  Constructors:
    Vector(void) {
        for (int i=0; i<DIMS; ++i) {
            component[i] = T();
        }
    }
    Vector(T x, T y, T z) {
        component[X] = x;
        component[Y] = y;
        component[Z] = z;
    }

    //  Destructor:
    ~Vector(void) { }

    //  Scaling:
    friend Vector<T> operator*(const Vector<T>& obj);
    Vector operator*(const T scale) {
        Vector<T> result = Vector<T>();

        for (int i=0; i<DIMS; ++i) {
            result.component[i] = component[i] * scale;
        }

        return result;
    }
};

template <typename T>
Vector<T> operator*(const Vector<T>& obj) {
    Vector<T> result = Vector<T>();

    for (int i=0; i<DIMS; ++i) {
        result.component[i] = obj.component[i] * this*;
    }

    return result;
}

In my main method, I have the following lines of code:

Vector<float> testVector1 = Vector<float>(1.0f, 0.0f, 0.0f);
Vector<float> testVector2 = Vector<float>(0.0f, 1.0f, 0.0f);
Vector<float> testVector3 = 10.0f * testVector1;
Vector<float> testVector4 = testVector2 * 10.0f;

Everything compiles fine except for one error: While the fourth line in main() works fine (multiplying the vector by a scalar) the third (multiplying a scalar into the vector) gives me the error:

Error 1 error C2677: binary '*' : no global operator found which takes type 'Vector<T>' (or there is no acceptable conversion)

My best guess on the issue is that the compiler doesn't know which primitive's * operator I'm trying to overload, and I can't directly tell it since the class won't know the type until it's passed into the template. Is there a way to accomplish what I'm trying to do, or must the template always follow the class for operator overloading?

Update: So I caught the bad attempt at a left-handed overload thanks to jwismar and others. The definition for the function within the class is now:

friend Vector<T> operator*(T scalar, const Vector<T>& obj);

And it's implementation is:

template <typename T> 
Vector<T> operator*(T scalar, const Vector<T>& obj) {
    Vector<T> result = Vector<T>();

    for (int i=0; i<DIMS; ++i) {
        result.component[i] = obj.component[i] * scalar;
    }

    return result;
}

The initial declaration of the overload above the class is now template <typename T> Vector<T> operator*(T scalar, const Vector<T>& obj);, but I get the same error regardless of whether it's commented out or not.

Now I'm on to a more specific question regarding templates and operator overloading. The compiler now balks at compile, though the error is now an unresolved external:

Error 1 error LNK2019: unresolved external symbol "class Vector<float> __cdecl operator*(float,class Vector<float> const &)" (??D@YA?AV?$Vector@M@@MABV0@@Z) referenced in function _main C:\Users\D03457489\Desktop\UVCTester\UVCTester\main.obj UVCTester

So the compiler is telling me it can find a definition for operator*(float, Vector<float>) but can't find the implementation. So the new question is: is that the result of another basic oversight on my part, or is it just not possible to use templates in this way to generate operator overloads where the left side of the operand is unknown?

J W
  • 43
  • 1
  • 5
  • You should do a search for operator overloading in SO or in google and read more about it before starting. – David Rodríguez - dribeas Aug 02 '12 at 01:46
  • Having spent two hours researching both operator overloading and template classes before writing this question, that is not a very helpful response. Everything I could find on Stack Overflow dealing with both concepts at once involved applying the operator to two objects of the templated class, rather than a primitive and an object of the templated class. – J W Aug 02 '12 at 21:12
  • @J W: http://stackoverflow.com/questions/3633549/operator-overloading-operator-in-c, for example has an example of operator overloaded for two different types. And that is just one of the first results of searching for 'operator overloading [c++]' in SO – David Rodríguez - dribeas Aug 02 '12 at 21:34
  • But that result also doesn't answer my question, since it doesn't have anything to do with the situation I'm trying to account for: overloading an operator when the left side is an unknown type due to the class in question being templated. I already know how to overload non-template classes. – J W Aug 02 '12 at 22:36
  • @J W: No, it does not exactly provide the code for your problem, but it provides the information that you need to build your own solution. At any rate, the comment was specific to this line of code `friend Vector operator*(const Vector& obj);`, that clearly indicates that you have not yet understood operator overloading basics (overloading as a member with one N-1 arguments or as a free function with N arguments for an N-ary operator) and *that* is present in **all** basic tutorials on operator overloading. – David Rodríguez - dribeas Aug 02 '12 at 23:27
  • I understand operator overloading basics perfectly fine, thank you. I also understand that I made a simple mistake I overlooked in my original attempt at implementing one, and corrected that mistake. That is exactly what having multiple eyes on debugging, and therefore this entire site, is all about. It is NOT about making useless (and baseless) assessments of other users' level of research and dismissing their question as a lack of effort on their part to make yourself feel superior. – J W Aug 03 '12 at 05:20
  • I spent 2 hours slogging through posts about operator overloading on Stack Overflow and none of them have addressed the specific question I am trying to solve: Is, or is it not, possible to overload an operator in C++ when the left side of the operator is unknown due to templating. – J W Aug 03 '12 at 05:20
  • @J W. If you have fixed that mistake, you should edit the question to avoid confusion. Is it possible to uverload an operator when the left side operator is *unknown*, yes. Operator overloading can be done as a free function (part of the basics) and the first argument there is the left hand side (basic). You just need to make that argument of the type you need. If it must be the same that instantiates the `Vector`, then make it `T` (as in the solution that jwismar provided yesterday --and not accepted). If it can be *anything*, then provide an extra template argument. – David Rodríguez - dribeas Aug 03 '12 at 12:36
  • ... just like with any other function. If you need an argument that will match any type, use a template with a template argument representing that function argument's type. Operator overloading is just providing a function with a special name that will be picked up by the compiler. Why is the answer below not been accepted? (I can guess that, but you should really reduce the code to a particular example and repost a question. Hint: the friend declaration does not befriend a template, but a free function see [this](http://stackoverflow.com/a/4661372/36565), but **do** correct the error above) – David Rodríguez - dribeas Aug 03 '12 at 12:40
  • Note: A very important part of this discussion is that your *real* problem now is unrelated to the question. The question is still showing a trivial error that is resolved in any basic tutorial, and while your *real* problem is not basic, you are not providing people with the real code, and thus the real problem to solve. – David Rodríguez - dribeas Aug 03 '12 at 12:50
  • I had already edited the question before you wrote any of that drivel. At any rate, the question is, and always has been, regarding the strange interaction between templates and operator overloads. If your intention is to save the good people of Stack Overflow time by discouraging repeated questions, then your behavior is very counterproductive, seeing as you _are_ wasting time, and complaining on a non-repeated question at that. – J W Aug 06 '12 at 20:33
  • @J W: There is no point in continuing discussing. The original question was ill-formulated, and as it stood the suggestion to get a tutorial was sound. The problem seems to have been while copying from the original problem to the question (where the first argument of the function seems to have been dropped), which lead to a question whose only answer is: *learn the basics*. It seems (after the fact) that the issue was with the question, not with the original problem, but I am not to blame the errors in the question. – David Rodríguez - dribeas Aug 06 '12 at 20:43
  • Perhaps not, but you are certainly the only one we can rightfully blame for your hostility and overassumptions. You've spent this entire discussion hanging on a technicality instead of helping to resolve the issue. I honestly don't even know why you're here if you're so dead set on approaching the question with a counterproductive attitude. – J W Aug 09 '12 at 03:37
  • I am not sure to follow you... did I not provide a couple of links to solutions to your different problems (as stated at the point in time where I posted the link?). From the very beginning I provided an answer to the question (although not your real problem, as two other people, all of us dedicated time to the wrong question trying to help you). You understood the original comment as hostile, I tried to explain why I wrote it (which you call a *technicality*). If you were offended, it was not my intention, and I have tried to explain my reasoning. – David Rodríguez - dribeas Aug 09 '12 at 03:47
  • You only attempted helpful advice after I responded to a post that contained only "You should have researched this and not posted here." Even when you did, your advice was unrelated to the actual question. When I pointed that out, you simply argued that they were relevant instead of trying to understand my question. With most of your posts, I can't help but get the sense that you aren't trying to read the questions thoroughly and are more interested in drawing attention to your other posts instead; all of your attempts at "help" have been links to unrelated answers written by you. – J W Aug 09 '12 at 07:50
  • there is no point in continuing further the discusssion, check the timestamps. I provided the answer at 1:44, then added a comment at 1:46 (the answer tackled the immediate problem in your question, but the code seemed to indicate that you needed a more in depth coverage of the subject). The first link I provided is to a question in which I have no answer. I added a link to one answer of mine on befriending operators in templates when you commented on one approach you had taken, as that particular answer has (my opinion) a good coverage of friendship in templates. – David Rodríguez - dribeas Aug 09 '12 at 12:07
  • Think whether you have the right impression of what happened or you are biased by your feelings (I understand that getting stuck with a problem can be frustrating), and if you want a piece of advice (you might want advice, probably not from me), be careful when you write your questions, if you had provided the update that you added one day after the question was posted from the beginning, chances are you would have got better answers and faster. – David Rodríguez - dribeas Aug 09 '12 at 12:10

4 Answers4

1

It's not the 4th line that has the error, it's the 3rd one. The compiler is looking for a built in that takes a float on the left-hand side, and it can't find one. You're going to need to define a free function like:

template <typename T> Vector<T> operator*(T scalar, const Vector<T>& obj);
jwismar
  • 12,164
  • 3
  • 32
  • 44
  • Yeah, like I said in the original question, I know it's the 3rd line that is causing the error. At any rate, I changed the definitions and implementation of the overload to what you said, and now I get: `Error 1 error LNK2019: unresolved external symbol "class Vector __cdecl operator*(float,class Vector const &)" (??D@YA?AV?$Vector@M@@MABV0@@Z) referenced in function _main C:\Users\D03457489\Desktop\UVCTester\UVCTester\main.obj UVCTester` I think it's a step in the right direction, but now the compiler doesn't seem to want to match the definition to the implementation. – J W Aug 02 '12 at 21:14
  • Sorry, I mis-read which line you thought was in error. I thought I saw "The fourth line in main gives me an error." So now, you have a linker error instead of a compiler error. This means that the compiler is perfectly happy with the function declarations you have, but it's not able to locate a definition for one of the functions that you have declared. In this case, it can't find a function template instance where typename T = float. – jwismar Aug 03 '12 at 18:03
  • Try adding an explicit intantiation of your free operator* for float. The declaration you add will look like: `template Vector operator*(float scalar, const Vector& obj);` – jwismar Aug 03 '12 at 18:12
1

operator* is a binary operator, it needs two arguments, which means that you can implement it as either a member function that is applied to the left hand side and takes an argument (the right hand side) or as a free function that takes two arguments. It is usually better to implement it as a free function. More so when you want to be able to implement double*Vector<T> since the left hand side is not a class. The signature of that overload would be:

template <typename T>
Vector<T> operator*( T d, Vector<T> v );

To which you might want to add:

template <typename T>
Vector<T> operator*( Vector<T> v, T d );
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
0

Try this function signature instead (not tested):

template <typename T, typename F>
Vector<T> operator*(const F &lhs, const Vector<T>& rhs);

It should allow for statements like:

auto vec = f * other_vec;

for any type F which has a defined operator of the kind:

template <typename F, typename T>
undetermined operator*(const F& lhs, const T &rhs);

where T is the type used for the Vector<T>, and the returned type can implicitly cast to T.

So the following would probably work:

long l;
float f;
int i;
//.......

Vector<float> v1;
v2 = l * v1;
v3 = f * v2;
v4 = i * v3;
Anthony
  • 12,177
  • 9
  • 69
  • 105
  • This would work; I could overload a version for float, double, and long double, and think of a clever way to handle casting to the various precisions within the overload implementations. I was more interested in finding a more general, template-based solution, though to be honest I'm not even sure that's possible. – J W Aug 03 '12 at 05:27
  • Something like this? Let me know if it works. No compiler handy right now. – Anthony Aug 03 '12 at 07:01
  • Managed to get it working as `template friend Vector operator*(F scalar, const Vector& obj)` with the implementation inline. For some reason separating the implementation from the class definition causes the compiler to complain that the operator is ambiguous and is implemented twice: it points to the definition within class declaring the function a friend, and the implementation down at the end, and seems to think they're both implementations. At any rate, I've got it working well enough for my purposes, so thank you for your time and patience! – J W Aug 06 '12 at 20:30
  • @JW: Consider looking [this answer](http://stackoverflow.com/a/4661372/36565) as to how you should declare friendship inside templates, the different options and semantics. – David Rodríguez - dribeas Aug 06 '12 at 20:42
  • @JW, seeing as you accepted this as your answer, how about an upvote? ;) – Anthony Aug 22 '12 at 02:03
0

You need to define a function that allows a scalar on the LHS of the *. This is relatively easy to implement, fortunately:

template <typename T>
Vector<T> operator*(const T scale, const Vector<T> &v) {
    Vector<T> result = v;
    return result * scale;
}

Your other function that did component wise multiplication between two Vectors was improperly declared and implemented. It doesn't need to be a friend:

/*friend*/ Vector<T> operator*(const Vector<T>& obj);

And you implement it as a method of the class:

template <typename T>
Vector<T> Vector<T>::operator*(const Vector<T>& obj) {
    Vector<T> result = Vector<T>();

    for (int i=0; i<DIMS; ++i) {
        result.component[i] = obj.component[i] * component[i];
    }

    return result;
}
jxh
  • 69,070
  • 8
  • 110
  • 193
  • I don't have a function doing component-wise multiplication of two `Vector`s. I have a dot product and cross product function that wasn't listed in the question's code, but those both work fine and are correctly implemented. I think you're confusing `Vector * Vector` with the * overload for multiplying by a scalar. – J W Aug 02 '12 at 21:24
  • @JW: Sorry, I make assumptions about the code I read when i don't fully understand it. The last function in the source listing you provided doesn't compile, and seems out of place, and if it is supposed to do a cross product, it needs to return a matrix, not a vector. Regards – jxh Aug 02 '12 at 21:32
  • Yeah, the function implemented outside of the class definition was my first attempt at overloading `operator*(T, Vector)` which is implemented improperly. I've since changed it based on jwismar's suggestion, and am now trying to resolve a related issue. I can see how that could be confusing; in hindsight it does look like I was trying to multiply to vectors. In semi-related semantics, the cross product of two vectors returns a vector perpendicular to both source vectors, not a matrix. Though, technically a vector can be considered a one-dimensional matrix, so you're right in a way. :) – J W Aug 02 '12 at 22:51
  • @JW: Doh! You're right, I always have this visual image of the determinant, and keep thinking its a matrix. Regards – jxh Aug 02 '12 at 22:54