Search code examples
c++templatesoperator-overloadingfriend-functioninline-functions

Friend function not callable from template class operator overload


I am trying to set up a class for representing 2-3D vectors. It's a template class that takes in a Coord type (integral/flaoting point representation of coordinates) and Dim (number of dimensions) as part of its template definition.

#pragma once

#include <iostream>
#include <array>
#include <type_traits>

#define DIM2        2
#define DIM3        3

typedef size_t Dimensions;

namespace Geo{

    template<class Coordinate_type, Dimensions dim = DIM3>
    class Vector{

        static_assert(std::is_arithmetic<Coordinate_type>::value, "Vector class can only contain Integral or Floating point types");
        static_assert(dim >= DIM2, "Vector dimension should be at least 2D");

        // friends

            // Functions
         #if 1
        template <class type, Dimensions d>
        friend Vector<type, d> CrossProduct(const Vector<type, d>& first, const Vector<type, d>& second);
        #else
        friend Vector<Coordinate_type, dim> CrossProduct(const Vector<Coordinate_type, dim>& first, const Vector<Coordinate_type, dim>& second);
        #endif
        
        std::array<Coordinate_type, dim> coords;

    public:
        // Constructors
        Vector() {}
        // Array Constructor
        Vector(std::array<Coordinate_type, dim> _coords) : coords(_coords) {}
        // 3D explicit constructor
        Vector(Coordinate_type _x, Coordinate_type _y, Coordinate_type _z) :  coords({_x, _y, _z}) {}
        Vector(Coordinate_type _x, Coordinate_type _y) :  coords({_x, _y}) {}

        inline Vector<Coordinate_type, dim> operator+(const Vector<Coordinate_type, dim>& other);
        inline Coordinate_type operator[](const int idx) const;
        inline bool operator<(const Vector<Coordinate_type, dim>& other) const;

        inline float Magnitude() const;
        inline int GetDimension() const;
        inline Vector<Coordinate_type, dim> Normalize() const;

    };

    template<class Coordinate_type, Dimensions dim>
    inline Coordinate_type Vector<Coordinate_type, dim>::operator[](int idx) const{
        return this->coords[idx];
    }

    template<class Coordinate_type, Dimensions dim>
    inline Vector<Coordinate_type, dim> Vector<Coordinate_type, dim>::operator+(const Vector<Coordinate_type, dim>& other){

    }

    template<class Coordinate_type, Dimensions dim>
    inline int Vector<Coordinate_type, dim>::GetDimension() const{ return this->coords.size(); }



    template<class Coordinate_type, Dimensions dim>
    inline float Vector<Coordinate_type, dim>::Magnitude() const{

        float ret = 0;
        for(int i = 0; i < this->coords.size(); i++){
            ret += (*this)[i] * (*this)[i];
        }

        return std::sqrt(ret);
    }


    template<class Coordinate_type, Dimensions dim>
    inline Vector<Coordinate_type, dim> Vector<Coordinate_type, dim>::Normalize() const{
        // Store magnitude of vector
        Coordinate_type magnitude = this->Magnitude();
        
        // Copy vector
        std::array<Coordinate_type, dim> temp;
        for(int i = 0; i < this->GetDimension(); i++){
            // Store the normalized component
            temp[i] = (*this)[i]/magnitude;
        }

        return Vector<Coordinate_type, dim>(temp);
    }

    template<class Coordinate_type, Dimensions dim>
    inline bool Vector<Coordinate_type, dim>::operator<(const Vector<Coordinate_type, dim>& other) const{

        // Call to CrossProduct function
        auto crossProd = CrossProduct( this->Normalize(), other.Normalize() );

        //  other logic ...
        return true;
    }


    template<class Coordinate_type, Dimensions dim>
    Vector<Coordinate_type, dim> CrossProduct( const Vector<Coordinate_type, dim>& first,  const Vector<Coordinate_type, dim>& second){
        
        // Definition here
        return Vector<Coordinate_type, dim> (first.coords[0], first.coords[1], first.coords[2]);
    }


}


-----------------------------------------

// main.cpp

int main() {

    Geo::Vector<int, 3> v1(1,2,3);
    Geo::Vector<int, 3> v2(2,4,6);

    if(v1 < v2){
        std::cout<< "v1 is less than v2" << std::endl;
    }

return 0;
}

It has operator overloads for comparing vectors. And I need to only compare parallel vectors on the basis of their their magnitudes.

To Check for parallelity, I use a cross product. Defined as a friend. But I keep getting an error when I try to call it from the '<' operator overload.

Error reads: Undefined symbols for architecture arm64: "jmk::CrossProduct(jmk::Vector<int, 3ul> const&, jmk::Vector<int, 3ul> const&)", referenced from: jmk::Vector<int, 3ul>::operator<(jmk::Vector<int, 3ul> const&) const in main.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [main] Error 1

The CrossProduct function is callable from main without any errors but for when it is called from the operator overload. it throws a compilation error.


Solution

  • You don't care of providing a Minimal, Reproducible Example, thus this is a solution with fixing missing parts.

    You declare a free friend function. Then you define a function template, and keep the free function undefined. #if 0 enables the code that is like your code and gets the linker error "undefined reference to CrossProduct()". #if 1 enables the fix.

    #include <iostream>
    
    using Dimensions = int;
    
    template <class Coordinate_type, Dimensions dim = 3>
    class Vector {
    #if 1
      template <class type, Dimensions d>
      friend Vector<type, d> CrossProduct(const Vector<type, d>& first,
                                          const Vector<type, d>& second);
    #else
      friend Vector<Coordinate_type, dim> CrossProduct(
          const Vector<Coordinate_type, dim>& first,
          const Vector<Coordinate_type, dim>& second);
    #endif
    
     public:
      // Constructors
      Vector() {}
    
      // 3D explicit constructor
      Vector(Coordinate_type _x, Coordinate_type _y, Coordinate_type _z) {}
    
      inline bool operator<(const Vector<Coordinate_type, dim>& other) const;
    };
    
    template <class Coordinate_type, Dimensions dim>
    inline bool Vector<Coordinate_type, dim>::operator<(
        const Vector<Coordinate_type, dim>& other) const {
      // Call to CrossProduct function
      auto crossProd = CrossProduct(*this, other);
      //  other logic ...
      return true;
    }
    
    template <class Coordinate_type, Coordinate_type dim>
    Vector<Coordinate_type, dim> CrossProduct(
        const Vector<Coordinate_type, dim>& first,
        const Vector<Coordinate_type, dim>& second) {
      // Definition here
      return first;
    }
    
    // main.cpp
    
    int main() {
      Vector<int, 3> v1(1, 2, 3);
      Vector<int, 3> v2(2, 4, 6);
    
      if (v2 < v1) {
        std::cout << "v1 is less than v2" << std::endl;
      }
    
      return 0;
    }
    

    The life example: https://godbolt.org/z/ExYcjKErM