1

I am quite new to C++ and I am implementing a, quite simple, (linear algebra) vector class as a template.

Now, I am trying to implement the addition and subtraction operators but with the ability to perform the operations not only between vectors, but also with scalars (short, long, float, double, etc.) (this will be an element-wise operation).

The relevant part of the code can be seen below and both declaration and definition (implementation) of the template class is in the same .hpp file (checked this question and didn't seem to provide a solution for this exact reason).

// Forward declaration of friend function
template<class S, class T>
Vector<T> operator +(const S& scalar, const Vector<T>& vec);

template<class T>
class Vector {
  public:
    Vector(unsigned long length);
    Vector(Vector<T> vec);

    template<class S>
    friend Vector<T> operator +(const S& scalar, const Vector<T>& vec);
    
    template<class S>
    Vector<T> operator +(const S& scalar) const;

    template<class S>
    Vector<T>& operator +=(const S& scalar);

  private:
    unsigned long mLen;
    T* mData;
};

// Constructor
Vector<T>::Vector(const unsigned long length) : mLen(length) {
  mData = new T[mData];

  for(unsigned long i = 0; i < mLen; ++i) {
    mData[i] = T();
  }
}

// Copy constructor
template<class T>
Vector<T>::Vector(Vector<T> vec) {
  mLen = 0;
  mData = nullptr;
  std::swap(mData, vec.mData);
}

// Implementation of the operators
template<class S, class T>
Vector<T> operator +(const S& scalar, const Vector<T>& vec) {
  return vec + scalar;
}

template<class T> template<class S>
Vector<T> operator +(const S& scalar) const {
  Vector<T> result(*this);
  return result += scalar;
}

template<class T> template<class S>
Vector<T>& Vector<T>::operator +(const S& scalar) {
  // mLen is the length of the object (member variable)
  for(unsigned long i = 0; i < mLen; ++i) {
    mData[i] += scalar; // mData is the array holding the values (member variable)
  }

  return *this;
}

The addition between a Vector and a scalar is implemented (as a member function though) and works as intended (result-wise). Furthermore, I can guarantee (for the moment at least) that S will always be one of short, int, float or double.

So, when in my tests I try to do something like

Vector<int> vec(10);

1 + vec;

I get the following error

Undefined symbols for architecture x86_64: "operator+(int const&, Vector const&)", referenced from: _main in main.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

I am working on XCode 11.3.1 for this project.

Any insights would be most welcome. Additionally, I know that my understanding of templates (and a whole lot more about C++) is still very limited, so if you would require more information to get a better idea please let me know.

Thanks in advance.

ZaellixA
  • 162
  • 1
  • 8
  • 1
    I'm guessing you have placed implementation of template inside cpp file. Templates have to be defined in header file. – Marek R Nov 03 '20 at 15:45
  • Can you show the syntax of addition of vector and scalar? – Harry Nov 03 '20 at 15:45
  • 2
    Please [edit] your question to show a [mcve]. Exactly *where* are you defining (implementing) the operator function? – Some programmer dude Nov 03 '20 at 15:47
  • 1
    @MarekR, no this is not the case, unfortunately. I also state that in the question that declaration and implementation reside in the same .hpp file. – ZaellixA Nov 03 '20 at 15:47
  • 2
    Don't bother with forward declaring the `operator+()` before the class definition that declares it as a friend. The definition will be `template template class Vector operator+(const S &scalar, const Vector &vec) {return vec + scalar;}`. If you really must forward-declare, do it the same way (but without the body). Don't combine nested templates (a templated member function of a templated class) using a single `template` keyword. – Peter Nov 03 '20 at 15:47
  • 1
    Possible explanation of problem: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file – Marek R Nov 03 '20 at 15:48
  • 1
    Did you consider that there is a difference between `1+vec` and `vec+1`? – MorningDewd Nov 03 '20 at 15:48
  • @Harry, yes I will edit the question accordingly. Thanks – ZaellixA Nov 03 '20 at 15:49
  • 4
    Take a look at the differing signatures: `Vector operator +(const S& scalar, const Vector& vec);` vs `friend Vector operator +(const S& scalar, Vector vec);`. `const Vector& vec` is not `Vector vec`. – Nathan Pierson Nov 03 '20 at 15:49
  • 1
    @MorningDewd yes. I have a different implementation for vec+1 which seems to work as intended (as I said, this is result-wise). – ZaellixA Nov 03 '20 at 15:51
  • 1
    @TedLyngmo yes, I am sorry I was late. Took me a while to edit the question. – ZaellixA Nov 03 '20 at 16:06
  • @NathanPierson thanks for that. This was a type in the question not in the original code. Thanks for pointing out though. – ZaellixA Nov 03 '20 at 16:07
  • @Peter Thanks for that. I'll give it a try and let you know if it worked out for me. I appreciate the help. – ZaellixA Nov 03 '20 at 16:08
  • Member `operator+` looks like it has some issues. It should be `const`, I don't understand why it takes an additional `Vector` as its argument, and if it takes a `const Vector` as its argument I don't understand how it successfully calls `operator +=` on it. – Nathan Pierson Nov 03 '20 at 16:11
  • @NathanPierson yes once more you are right... I have a type there. The vector parameter should not be present... Let me copy and paste this time to make sure I get it right ;(. – ZaellixA Nov 03 '20 at 16:13

1 Answers1

4

First, the free function template doesn't need to be a friend since it's not directly accessing the private members in Vector<T>. It also doesn't need to be forward declared. This can also be greatly simplified by using a std::vector<T> to keep the data to not have to implement the rule of 5 manually.

Example:

#include <vector>

template<class T>
class Vector {
public:
    Vector(unsigned long length) :
        mData(length) // create the vector
    {}

    template<class S>
    Vector& operator+=(S scalar) {
        for(auto& v : mData) v += scalar;  // add "scalar" to all elements in the vector
        return *this;
    }

    template<class S>
    Vector operator+(S s) const {
        Vector copy(*this);       // using the implicitly defined copy constructor
        copy += s;                // use the above operator+=
        return copy;
    }

private:
    std::vector<T> mData;
};

template<class S, class T>
Vector<T> operator +(S scalar, const Vector<T>& vec) {
    return vec + scalar;
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • I think there is no need of `return *this` in `+=` operator overloading. It can be made to return `void`. `void operator+=(S scalar)` – Harry Nov 03 '20 at 16:22
  • 2
    @Harry Yes, that's correct, but then one couldn't do chained operations like `return copy += s;` (which I didn't even use myself). It's a matter of taste I guess. – Ted Lyngmo Nov 03 '20 at 16:24
  • 4
    @Harry It's actually a little more than a matter of taste. operators should have their usual meaning. Since you can chain `+=` generally, it's going to be surprising to users of your class if they can't do that, and you should try to avoid surprising your users :) – cigien Nov 03 '20 at 16:25
  • @cigien I agree so I always `return *this;` but some teachings here on SO calls that to code without thinking. I read some strong opinions _against_ `return *this;` if it's not needed the other day written by some high-rep guy ... but **I** like it so I'll keep coding those parts without thinking :-) – Ted Lyngmo Nov 03 '20 at 16:29
  • 3
    Hmm, I've never heard of any reason to do that except "it's not strictly necessary" :p If you remember where you saw that, let me know, I'd love to see the reasoning :) And yeah, I personally recommend continuing to do it the idiomatic way. – cigien Nov 03 '20 at 16:42
  • @cigien I'll try to remember to make a note next time I stumble across that :) – Ted Lyngmo Nov 03 '20 at 16:43
  • 3
    Thanks :) I maybe missing something I've never thought of, so it'd be great to find out :) – cigien Nov 03 '20 at 16:44
  • I am not sure what exactly was going on. Your solution seems to work (to be honest I stayed with the arrays insted of std::vector but that's unrelated to the question). I had to make the operator non-member and non-friend in order to make it work, which to be honest I am not sure why this is the case. If any of you guys have any idea about it I would be delighted to find out. No matter what, thank you for all the help. – ZaellixA Nov 03 '20 at 17:12
  • 1
    @ZaellixA Hmm, odd, Perhaps you can create a new question with that part in and display the code and what you'd like to do? Sidenote: I just noticed a bug in your code that I didn't think about before (since I used the compiler generated copy constructor): `mLen = 0;` should be `mLen = vec.mLen;` in your copy constructor. But I wonder how it works? The signature `Vector(Vector vec)` relies on a copy constructor? – Ted Lyngmo Nov 03 '20 at 17:19
  • Yeah that's true. Actually I have a member function in my code that swaps the members correctly, but I wanted to provide something very simple here to avoid cluttering and I was hasty and wrote it wrong. Thanks a million for all the help though. I'll try to work more on the error and if I don't manage to find what it was I'll post another question. Thanks again – ZaellixA Nov 03 '20 at 17:32