0

I created a simple matrix definition like so:

template <size_t m, size_t n>
using Matrix = boost::array<boost::array<float, n>, m>;

I wish to overload the [] operator so that I can access the matrix values with matrix[i, j] instead of matrix[i][j]. I tried the following but to no avail.

template <size_t m, size_t n>
float& Matrix<m,n>::operator[](uint32_t i, uint32_t j) {
    return &(this[i][j]);

I could not make sense of partial template specialization, and generally do not understand why I can't overload the operator.

clang++ 10.0.1 compile output is

matri.cpp:10:21: error: nested name specifier 'Matrix<m, n>::' for declaration does not refer into a class, class template
 or class template partial specialization
float& Matrix<m,n>::operator[](uint32_t i, uint32_t j) {
       ~~~~~~~~~~~~~^
matri.cpp:11:14: error: invalid use of 'this' outside of a non-static member function
    return &(this[i][j]);
             ^
matri.cpp:11:19: error: use of undeclared identifier 'i'
    return &(this[i][j]);
                  ^
matri.cpp:11:22: error: use of undeclared identifier 'j'
    return &(this[i][j]);
Utku Boduroglu
  • 127
  • 1
  • 7
  • 1
    Alas, `operator[]` only take one parameter. You could make the parameter a `pair` ... but that probably defeats the purpose of clearer code. – Eljay Aug 27 '20 at 16:57
  • For this to work, the class `boost::array, m>` must declare this operator overload. There is no such overload defined in the `boost::array` class, and you simply can't define it, in an ad-hoc fashion, like this. C++ does not work this way. Not to mention that the `[]` overload always takes one parameter. – Sam Varshavchik Aug 27 '20 at 16:57
  • @SamVarshavchik True, but that may change in the future (there is at least one proposal: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2128r1.pdf ). – Bob__ Aug 27 '20 at 19:21
  • Buried in [What are the basic rules and idioms for operator overloading?](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading/4421715#4421715): the `operator[]` that can be overloaded is binary infix, as in `A[x]` -- one operand before the bracket, and *one*, not two, inside the brackets. – JaMiT Aug 27 '20 at 21:58
  • Does this answer your question? [What are the basic rules and idioms for operator overloading?](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) – Daniel Widdis Aug 27 '20 at 23:48

1 Answers1

0

There are multiple issues in this approach.

  • Since Matrix is only an alias, you are trying to partially specialize a member function (operator[]) of boost::array, but you can only partially specialize the entire class template, not just a member function.

  • operator[] is binary (only one of the operands must be inside the brackets) and that can't change when it is overloaded in a user-defined class. If you don't like chaining those operators, you could overload operator() instead.

The following shows a minimal implementation of an user-defined Matrix class. If you need something more, consider using one of the already existing linear algebra libraries.

#include <array>
#include <initializer_list>
#include <iostream>
#include <numeric>

template <class Type, size_t Rows, size_t Cols>
class Matrix
{
    std::array<Type, Rows * Cols> m_;
public:
    Matrix() = default;
    Matrix(std::initializer_list<std::initializer_list<Type>> src) {
        auto dest{ m_.begin() };
        for ( auto const& row : src ) {
            std::copy(row.begin(), row.end(), dest);
            dest += Cols;
        }
    }
    static constexpr size_t rows() {
        return Rows;
    }
    static constexpr size_t columns() {
        return Cols;
    }
    auto begin() {
        return m_.begin();
    }
    auto begin() const {
        return m_.begin();
    }
    auto end() {
        return m_.end();
    }
    auto end() const {
        return m_.end();
    }
    Type operator() (size_t i, size_t j) const { 
        return m_[i * Cols + j];
    }
    Type& operator() (size_t i, size_t j) { std::cout << '*';
        return m_[i * Cols + j];
    }
};

template< class T, size_t R, size_t C >
std::ostream& operator<< (std::ostream& os, Matrix<T, R, C> const& m) {
    for ( size_t i{}; i < R; ++i ) {
        for ( size_t j{}; j < C; ++j ) {
            os << ' ' << m(i, j);
        }
        os << '\n';
    }
    return os;
}

int main() {
    Matrix<float, 3, 4> a{};
    std::iota(a.begin(), a.end(), 1);
    std::cout << a << '\n';

    Matrix<double, 3, 4> b {
        {1, 4, 7, 10},
        {2, 5, 8, 11},
        {3, 6, 9, 12}
    };
    std::cout << b << '\n';
}
Bob__
  • 12,361
  • 3
  • 28
  • 42