Search code examples
c++classgenericslinkerfriend

Linker Error when calling operator<<, how to fix this?


Important Update: Removing the delegation of friend solved the problem Partially but why? and how may I keep it as friend...

Why the following code gets me linker error?

Dimensions dims2(3 ,14);//Fixed class 100% the bug isn't cause by it
Matrix<int> mat_2(dims2, 5);
std::cout << mat_2;

My class:

template<class T>
class Matrix {
public:
    friend std::ostream &operator<<(std::ostream &os, const Matrix<T> &matrix);

;}

in .h file I have:

template<typename T>
std::ostream &operator<<(std::ostream &os, const Matrix<T> &matrix) {}

I get the following:

Undefined symbols for architecture x86_64:
"mtm::operator<<(std::__1::basic_ostream >&, mtm::Matrix const&)", referenced from: _main in main.cpp.o ld: symbol(s) not found for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)


Solution

  • friend std::ostream &operator<<(std::ostream &os, const Matrix<T> &matrix);
    

    This declares a non-template function named operator<< (in the namespace enclosing Matrix definition). That function is never defined. In addition, a function template also named operator<< is defined. When doing overload resolution, the compiler prefers a non-template over the template, then the linker discovers there's no definition.

    There are several ways to resolve this. One is to define the operator in-class:

    template<class T>
    class Matrix {
        friend std::ostream& operator<<(std::ostream& os, const Matrix<T>& matrix) {
          // Implementation here
        }
    };
    

    Another is to befriend the function template:

    template <typename T> class Matrix;
    
    template<typename T>
    std::ostream &operator<<(std::ostream &os, const Matrix<T> &matrix);
    
    template<class T>
    class Matrix {
        friend std::ostream& operator<< <T>(std::ostream &os, const Matrix<T> &matrix);
    };
    

    Yet third is to not require friendship at all, e.g. like this:

    template<class T>
    class Matrix {
    public:
        // Actual implementation here.
        void PrintMe(std::ostream &os);
    };
    
    template<typename T>
    std::ostream &operator<<(std::ostream &os, const Matrix<T> &matrix) {
      matrix.PrintMe(os);
      return os;
    }