0

I am trying to write an output function for a matrix class by overloading the operator<< overload. The matrix class also has an indexing function, which is created by overloading the `op.

The code is as follows:

template<typename T, unsigned int N>
T& LU_Matrix<T, N>::operator() (unsigned int i, unsigned int j)
{
    return data_[N * i + j];
}

template<typename T, unsigned int N>
std::ostream& operator<< (std::ostream& os, const LU_Matrix<T, N> mat)
{
    for (unsigned int i = 0; i < N; ++i)
    {
        for (unsigned int j = 0; j < N; ++j)
        {
            os << mat(i, j) << " ";
        }
        os << std::endl;
    }
    return os;
}

int main()
{
    LU_Matrix<double, 3> lu_mat();
    // instantiate lu_mat here
    std::cout << lu_mat << std::endl;
    return 0;
}

When I run this, it throws the error

no match to call for '(const LU_Matrix<double, 3>) (unsigned int, unsigned int)'

It seems like the compiler should have created the stated function, given the templated function overloading operator<<. What's going wrong here?

JeJo
  • 30,635
  • 6
  • 49
  • 88
Lucas Myers
  • 366
  • 2
  • 13
  • 6
    Your `operator()` isn't `const-qualified`, therefore it cannot be used with `const LU_Matrix` – Lala5th Aug 04 '21 at 19:31
  • 4
    You also want `const LU_Matrix & mat`. – n. m. could be an AI Aug 04 '21 at 19:32
  • @Lala5th how would I const-qualify the operator? – Lucas Myers Aug 04 '21 at 19:33
  • 2
    @LucasMyers If I remember correctly you would want `const T& LU_Matrix::operator()(unsigned int, unsigned int) const` as the function signature. The second `const` does the heavy lifting. More details: https://en.cppreference.com/w/cpp/language/member_functions#const-_and_volatile-qualified_member_functions – Lala5th Aug 04 '21 at 19:35
  • 1
    @LucasMyers `LU_Matrix` declares `T& operator()(unsigned int, unsigned int)`. You need one declared, and implemented, as `T const& operator()(unsigned int, unsigned int) const`. You'll end up with two `operator()` decls and impls; one for const instances, the other for mutable instances (and you don't need the latter if you never use `operator()` to mutate the instance; none of the code presented seems to, btw). – WhozCraig Aug 04 '21 at 19:37
  • @WhozCraig I am a little bit confused by what you mean when you say "mutable instances". The `()` operator is acting as a matrix subscriptor, and even when I add the `const` keyword after the `operator()` declaration (*not* before) it lets me write values to the given matrix entries via `lu_mat(i, j) = 5.0` for example. However, when I make that change it *also* does not throw an error for the declaration of my `<<` overload. – Lucas Myers Aug 04 '21 at 19:45
  • Your original non-const version will indeed let you do that (`lu_mat(i, j) = 5.0`); no such actual code *does* that in the posted, incomplete sample. If you need that *and* you use a const instance (or const reference, which is preferred) to `operator <<`, then you need *both* versions of operator() (const and non-const). Of course, alternatively, you can implement `operator <<` to not use `operator()` at all. – WhozCraig Aug 04 '21 at 19:48

1 Answers1

2

What's going wrong here?

As others have pointed out in comment section, Your LU_Matrix::operator() isn't const-qualified, therefore it cannot be used with const qualified LU_Matrix objects.

In your operator<< overload, you have the above-mentioned case:

std::ostream& operator<< (std::ostream& os, const LU_Matrix<T, N> mat)
//                                          ^^^^^^ --------------------> this

Here the compiler does not find a const operator() operator overload to call with the const object, hence the error!

You need to change the declaration and definition to:

const T& operator() (unsigned int i, unsigned int j) const;
^^^^^^^^^                                            ^^^^^^

Not that, the return parameter also must be const qualified, as you return the element from a member of the class from a const member function.


In addition, as an improvement operator<<, neither modify the object nor require a copy, therefore you can actually pass const-ref to LU_Matrix<T, N> to the operator<< overload:

template<typename T, unsigned int N>
std::ostream& operator<< (std::ostream& os, const LU_Matrix<T, N>& mat) /* noexcept */
{
    // ...
    return os;
}

(See a demo)

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Okay, let me make sure I understand correctly: the `const` qualifier in the parameter list of `operator<<` indicates that the `LU_Matrix` object will not be changed within the function. However, that means that all the member functions used within the `operator<<` function need to be labeled as "not modifying the `LU_Matrix` object". This labeling is done with the `const` keyword after a function's parameter list. Indeed, if I add in that addition to give `T& operator() (unsigned int i, unsigned int j) const` the program compiles and gives the correct result. – Lucas Myers Aug 05 '21 at 02:02
  • However, you suggest I also add the `const` keyword to the beginning of the `operator()` declaration. This just indicates that the return value of that function (which happens to reference member data of the `LU_Matrix` object) will not be changed, correct? This seems reasonable, given that I don't want my `LU_Matrix` object to change in the body of the `operator<<` function, but the compiler doesn't complain if I don't put the `const` keyword out front of the `operator()` function. Indeed, I can change the `data_` field in my `LU_Matrix` object. This seems wrong. Is this UB? – Lucas Myers Aug 05 '21 at 02:06