0

I have a matrix class that supports operations with scalar values that I implemented using operator overloading. Since each overloaded operator function has the same body except for the operator being used, I decided to create a generic function that would accept a function along with the matrix and a scalar value to consolidate my code.

Here is the generic function and it being called from the overloaded addition operator function:

    // Generic function to take care of matrix operations
    template<typename T, typename F>
    Matrix<T> scalar_operation(const Matrix<T> &a, const T b, F f) {
        std::vector<std::vector<T> > new_els = a.elements;
        typename std::vector<std::vector<T> >::iterator it = new_els.begin();
        typename std::vector<T>::iterator it_in;

        for (; it != new_els.end(); ++it) {
            it_in = it->begin();
            for (; it_in != it->end(); ++it_in) {
                *it_in = f(*it_in, b);
            }
        }
        return Matrix<T>(new_els);
    }

    // Add scalar to all values in Matrix
    template<typename T>
    Matrix<T> operator+(const Matrix<T> &a, const T b) {
        return scalar_operation(a, b, std::plus<T>()); 
    }

And here are the functions declared in the matrix class:

    template<class T>
    class Matrix {
        template<typename F>
        friend Matrix<T> scalar_operation(const Matrix<T> &a, const T b, F f);

        friend Matrix<T> operator+<>(const Matrix<T> &a, const T b);
        friend Matrix<T> operator-<>(const Matrix<T> &a, const T b);
        friend Matrix<T> operator*<>(const Matrix<T> &a, const T b);
        friend Matrix<T> operator/<>(const Matrix<T> &a, const T b);

When I implemented the overloaded operator functions separately, they worked, but with this implementation, I get the following compiler error:

    Undefined symbols for architecture x86_64:
    "Matrix<float> scalar_operation<std::__1::plus<float> >(Matrix<float> const&, float, std::__1::plus<float>)", referenced from:
        Matrix<float> operator+<float>(Matrix<float> const&, float) in matrix_test-3188cd.o
    ld: symbol(s) not found for architecture x86_64

I imagine my error is related to the Matrix<float> scalar_operation<std::__1::plus<float> >( line because it looks like the linker is searching for a function with this header, which is slightly different from how it's declared in my file, but I've tried modifying my declaration and it throws additional errors.

Why am I getting this error and how can I fix it? Thanks!

EDIT: To clear up some confusion, all the code has been implemented in the header file since it is a templated class. There is no corresponding .cpp file.

jlesniak
  • 3
  • 3
  • Did you place template function implementation in .cpp-file? – Abstraction Mar 22 '17 at 15:18
  • you declared a friend `template friend Matrix scalar_operation(const Matrix &a, const T b, F f);` but there is no such function, you have only `template Matrix scalar_operation(const Matrix &a, const T b, F f);` – em2er Mar 22 '17 at 15:39
  • @em2er so does the line `template` before the Matrix class body not work? Because with the overloaded operator functions, I don't declare them with a `template` inside the body of the Matrix class, but they still work – jlesniak Mar 22 '17 at 16:59
  • 1
    `template friend scalar_operation` declares friend function with one template parameter, the code you provided contains no such functions, but it has `template scalar_operation`, that does not fit to your friend declaration. friend operator+<> fits to template operator+ so there is no problem. Declare `template friend Matrix scalar_operation(const Matrix &a, const T b, F f);` i think it is the solution. – em2er Mar 22 '17 at 17:16
  • @em2er Thanks! Your method worked, I just had to use a variable other than 'T' so the compiler wouldn't confuse it with `template` outside of the Matrix class body – jlesniak Mar 22 '17 at 17:23
  • @Someprogrammerdude the question is not a duplicate, read comments above. Please remove duplication flag – em2er Mar 22 '17 at 17:32
  • The duplicates may initially seem obscure, but they sole the issue. You have effectively told the compiler there is a function suitable for compilation (declaration) and linkage, the compilation succeeds, but the function doesn't exist (there is no definition; declaration and definition are not the same thing) - thus the linker fails. Template parameters matter in cases such as these, so does the functions definition. – Niall Mar 22 '17 at 18:00
  • @em2er this has a slight downside of declaring every instantiation of scalar_operation a friend, instead of just those that take a `Matrix`. – n. m. could be an AI Mar 23 '17 at 09:00
  • @n.m. Do you mean that `scal_op` will get private access for `Matrix`? – em2er Mar 23 '17 at 09:07
  • @em2er yes exactly – n. m. could be an AI Mar 23 '17 at 09:32
  • @n.m. if you have a good solution to avoid this, it will be very nice if you post it as answer. I have no idea how to resolve this without code redesign. – em2er Mar 23 '17 at 09:46
  • @em2er I don't, wonder if there is one. – n. m. could be an AI Mar 23 '17 at 10:14

1 Answers1

0

The code has the following problem: class Matrix declares the friend function friend template<typename F> scalar_operation(...) (yes, friend keyword not only states the private access, it also declares a function), but there is no such function in outer scope. There is only the template<typename T, typename F> scalar_operation, that does not satisfy friend Matrix's declaration. Why? Let's try to pseudocode instantiations that can be made with float and std::plus<float> (return values and arguments omitted for shortness):
friend scalar_operation<std::plus<float>>(...)
and
scalar_operation<float, std::plus<float>>(...).
As we can see they are different. So during compilation we have no problem: we have proper friend scalar_operation declaration that satisfies calling convention inside operator+. But no definitions of such declaration can be made, and we get a problem while linking. The solution is a proper friend declaration:
template<typename TT, typename F> friend Matrix<TT> scalar_operation(const Matrix<TT> &, const TT, F);

em2er
  • 811
  • 5
  • 15