0

I'm trying to make a simple 2D coordinate vector class for a game, but I've run into a problem when overriding operator+. The vector uses a template to set the data type of its X and Y values, so all of the functions, including operator overloads, must use the template. All of the member functions work fine, but the non-member ones give me an error.

Here is the error:

In function `ZNK13AxisAlignedBB8maxPointEv':
undefined reference to `Vector<double> operator+<double>(Vector<double> const&, Vector<double> const&)'

Here is the piece of code that gives me the error:

Vector2d AxisAlignedBB::maxPoint() const {
    return position + dimensions;
    //position and dimensions are both type Vector<double>
}

Here is the declaration of the function:

template <typename T> class Vector {
    //class definition...
}


template<typename T>
Vector<T> operator+(const Vector<T>&, const Vector<T>&);

Here is the implementation of the function. As far as I can tell, it should match the declaration above:

template<typename T> Vector<T> operator+(const Vector<T>& v1, const Vector<T>& v2){
    return v1.add(v2); //returns Vector<T>(v1.x + v2.x, v1.y + v2.y)
}

I've also been using SFML for the graphics in my game, so just to make sure, I looked at SFML/System/Vector2.hpp, which does essentially the same job as my vectors. Here is there version of the same function, which looks identical to mine except for the argument names:

template <typename T>
Vector2<T> operator +(const Vector2<T>& left, const Vector2<T>& right);

I don't have access to SFML's implementation of the function, so I can't check to make sure it's the same. The reason I'm not using SFML's vectors to begin with is because they don't include functions for magnitude, cross product, etc. Also, I'm really lazy and don't want to put "sf::" in front of everything.

EDIT: Full text of Vector.h/.cpp

Vector.h:

#ifndef VECTOR_H
#define VECTOR_H

#include <SFML/System/Vector2.hpp>

template <typename T> class Vector {
public:
    T x;
    T y;

    Vector<T>() : x(0), y(0) {};

    Vector<T>(T ax, T ay) : x(ax), y(ay) {};

    ///Conversion from SFML vector of same type
    Vector<T>(const sf::Vector2<T>& v) : x(v.x), y(v.y) {};

    /**
     * Returns the magnitude of this Vector<T>.
     */
    T mag() const;

    /**
     * Returns the square of the magnitude of this Vector<T>, cutting
     * out an expensive sqrt() call.
     */
    T magSquared() const;

    /**
     * Returns the dot product of this and the given Vector<T>.
     */
    T dot(const Vector<T>&) const;

    /**
     * Returns the distance between this and the given Vector<T>.
     */
    T distance(const Vector<T>&) const;

    /**
     * Returns the square of the distance between this and the given Vector<T>,
     * cutting out an expensive sqrt() call.
     */
    T distanceSquared(const Vector<T>&) const;

    /**
     * Returns a new Vector<T> with the same coordinates.
     */
    Vector<T> clone() const;

    /**
     * Returns the resultant Vector<T> of adding the given Vector<T>.
     */
    Vector<T> add(const Vector<T>&) const;

    /**
     * Returns the resulting Vector<T> of subtracting the given Vector<T>.
     */
    Vector<T> sub(const Vector<T>&) const;

    /**
     * Returns the resulting Vector<T> of multiplying this Vector<T> by
     * the given scalar value.
     */
    Vector<T> mult(T) const;

    /**
     * Returns the projection of this Vector<T> onto the given axis.
     * In this function, this Vector<T> is the point to be projected
     * and the given Vector<T> is the axis.
     */
    Vector<T> proj(const Vector<T>&) const;

    /**
     * Returns the projection of the given Vector<T> on to this axis.
     * In this function, this Vector<T> acts as the axis and the given
     * Vector<T> is the point to be projected.
     */
    Vector<T> proj2(const Vector<T>&) const;

    /**
     * Revolves this Vector<T> about the origin by the given angle in radians.
     */
    Vector<T> revolve(T) const;

    /**
     * Revolves this Vector<T> about the given Vector<T>
     * by the given angle in radians.
     */
    Vector<T> revolveAbout(T, const Vector<T>&) const;

    /**
     * Returns the normalized version of this Vector<T>.
     */
    Vector<T> normalize() const;

    /**
     * Performs a 2D cross product with the given Vector<T>.
     */
    T crossVector(const Vector<T>&) const;

    /**
     * Performs a 2D cross product with the given scalar value.
     * This function performs the operation this X value.
     */
    Vector<T> crossScalar(T) const;

    /**
     * Performs a 2D cross product with the given scalar value.
     * This function performs the operation value X this.
     */
    Vector<T> crossScalar2(T) const;

    Vector<T>& operator+=(const Vector<T>& v){
        x += v.x;
        y += v.y;
        return *this;
    }

    Vector<T>& operator-=(const Vector<T>& v){
        x -= v.x;
        y -= v.y;
        return *this;
    }

    Vector<T>& operator*=(const T t){
        x *= t;
        y *= t;
        return *this;
    }

    Vector<T>& operator/=(const T t){
        x /= t;
        y /= t;
        return *this;
    }

    Vector<T> operator-(){
        return Vector(-x, -y);
    }

    ///Conversion from SFML vector of same type
    Vector<T>& operator=(const sf::Vector2<T>&);

    ///Conversion to SFML vector of same type
    operator sf::Vector2<T>();

protected:
private:
};

typedef Vector<double> Vector2d;
typedef Vector<float> Vector2f;
typedef Vector<int> Vector2i;

template<typename T> Vector<T> operator+(const Vector<T>& v1, const Vector<T>& v2){
    return Vector<T>(v1.x + v2.x, v1.y + v2.y);
}

template<typename T> Vector<T> operator-(const Vector<T>& v1, const Vector<T>& v2){
    return Vector<T>(v1.x - v2.x, v1.y - v2.y);
}

template<typename T> Vector<T> operator*(const Vector<T>& v, const T t){
    return Vector<T>(v.x * t, v.y * t);
}

template<typename T> Vector<T> operator*(const T t, const Vector<T>& v){
    return Vector<T>(v.x * t, v.y * t);
}

template<typename T> Vector<T> operator/(const Vector<T>& v, const T t){
    return Vector<T>(v.x / t, v.y / t);
}

#endif // VECTOR_H

Vector.cpp:

#include "Vector.h"
#include <cmath>

template<typename T> T Vector<T>::mag() const {
    return std::sqrt(magSquared());
}

template<typename T> T Vector<T>::magSquared() const {
    return std::pow(x, 2) + std::pow(y, 2);
}

template<typename T> T Vector<T>::dot(const Vector<T>& v) const {
    return (v.x * x) + (v.y * y);
}

template<typename T> T Vector<T>::distanceSquared(const Vector<T>& v) const {
    return sub(v).magSquared();
}

template<typename T> T Vector<T>::distance(const Vector<T>& v) const {
    return sub(v).mag();
}

template<typename T> Vector<T> Vector<T>::clone() const {
    return Vector(x, y);
}

template<typename T> Vector<T> Vector<T>::add(const Vector<T>& v) const {
    return Vector(x + v.x, y + v.y);
}

template<typename T> Vector<T> Vector<T>::sub(const Vector<T>& v) const {
    return Vector(x - v.x, y - v.y);
}

template<typename T> Vector<T> Vector<T>::mult(T t) const {
    return Vector(t * x, t * y);
}

template<typename T> Vector<T> Vector<T>::proj(const Vector<T>& axis) const {
    return axis.proj2(*this);
}

template<typename T> Vector<T> Vector<T>::proj2(const Vector<T>& point) const {
    T p = dot(point) / magSquared();
    return mult(p);
}

template<typename T> Vector<T> Vector<T>::revolve(T a) const {
    a += std::atan2(y, x);
    return Vector(mag() * std::cos(a), mag() * std::sin(a));
}

template<typename T> Vector<T> Vector<T>::revolveAbout(T a, const Vector<T>& v) const {
    return sub(v).revolve(a).add(v);
}

template<typename T> Vector<T> Vector<T>::normalize() const {
    return Vector(x / mag(), y / mag());
}

template<typename T> T Vector<T>::crossVector(const Vector<T>& v) const{
    return x * v.y - y * v.x;
}

template<typename T> Vector<T> Vector<T>::crossScalar(T t) const {
    return Vector(t * y, -t * x);
}

template<typename T> Vector<T> Vector<T>::crossScalar2(T t) const {
    return crossScalar(-t);
}

template<typename T> Vector<T>& Vector<T>::operator=(const sf::Vector2<T>& v){
    x = v.x;
    y = v.y;
    return *this;
}

template<typename T> Vector<T>::operator sf::Vector2<T>(){
    return sf::Vector2<T>(x, y);
}
  • 2
    Have you put the implementation of the function into the header, too? – delphifirst Mar 01 '15 at 01:42
  • 1
    probably duplicate of [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – M.M Mar 01 '15 at 01:46
  • I hadn't originally, but I moved it after reading this question: [link](http://stackoverflow.com/questions/4014294/operator-overloading-on-class-templates). Doing that fixed the first problem, but now I get the same error for non-operator functions as well. –  Mar 01 '15 at 01:48
  • add() is const and returns a new instance of Vector. –  Mar 01 '15 at 01:49
  • OK.. that's a rather unintuitive interface; normal style would be for `add` to have the same effect as `operator+=`, i.e. update the object it's being called on. – M.M Mar 01 '15 at 01:51
  • 1
    There's no difference between operator functions and non-operator functions other than their name. have you placed all implementations of template functions in the headers and made sure they are visible from the point where you are calling the function? If you are still having trouble then you will have to [post code that shows the problem](http://stackoverflow.com/help/mcve) as trying to describe the code is too vague – M.M Mar 01 '15 at 01:53
  • "There's no difference between operator functions and non-operator functions other than their name" Ah, that's where I was getting confused. Thanks for the help. –  Mar 01 '15 at 02:02

0 Answers0