0

I want to design a Matrix class which allows to specify the method of memory management, i.e.

Matrix<Allocator::vector, int> mv(2, 2);
Matrix<Allocator::unique_pointer, int> mu(2, 2);

mv(1, 1) = 1;
mu(1, 2) = 1;

and with mv and mu being compatible to each other (i.e. when I overload the "+" operator) despite different memory strategies.

I found already very good help from Class template specializations with shared functionality which does the same for an n dimensional vector class.

using dim_t = std::pair<size_t, size_t>;

enum class Allocator { vector, raw_pointer, unique_pointer };

template <typename T>
class MatrixBase {
 public:
  MatrixBase() : MatrixBase(0, 0){};
  MatrixBase(size_t m, size_t n) : dim_(m, n){};

  size_t rows() const;

  virtual T& at(size_t i, size_t j);

 private:
  dim_t dim_;
};

template <typename T>
size_t MatrixBase<T>::rows() const {
  return dim().first;
}

template <Allocator A, typename T>
class Matrix : public MatrixBase<T> {};

template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
 private:
  std::vector<T> data_;
};

template <typename T>
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
  return data_[i * rows() + j];
}

template <typename T>
class Matrix<Allocator::unique_pointer, T> : public MatrixBase<T> {
 private:
  std::unique_ptr<T[]> data_;
};

template <typename T>
T& Matrix<Allocator::unique_pointer, T>::at(size_t i, size_t j) {
  return data_[i * rows() + j];
}

Unfortunately the compiler complains

./matrix.hpp:100:34: error: out-of-line definition of 'at' does not match any declaration in 'Matrix<linalg::Allocator::vector,
      type-parameter-0-0>'
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
  ^
./matrix.hpp:103:20: error: use of undeclared identifier 'rows'
  return data_[i * rows() + j];

I assume the error originates from

template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
 private:
  std::vector<T> data_;
};

How do I fix this?

bodokaiser
  • 15,122
  • 22
  • 97
  • 140

2 Answers2

1

Move the definition inside will solve the problem. This problem is due to scoping.

template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
public: // or private:
    T& at(size_t i, size_t j) {
        return data_[i * MatrixBase<T>::rows() + j];
    }
private:
    std::vector<T> data_;
};

Consider the following example

class P {
    public:
        virtual int test();
};

class C : public P {
};

int C::P:: test() { // if you omit P, it will not compile
    return 21;
}

Or you can re-declare it inside C, so it will be in C's scope.

CS Pei
  • 10,869
  • 1
  • 27
  • 46
1

I have fixed Your code. Here You go:

using dim_t = std::pair<size_t, size_t>;

enum class Allocator { vector, raw_pointer, unique_pointer };

template <typename T>
class MatrixBase {
 public:
  MatrixBase() : MatrixBase(0, 0){};
  MatrixBase(size_t m, size_t n) : dim_(m, n){};

  size_t rows() const;

  virtual T& at(size_t i, size_t j);

 private:
  dim_t dim_;
};

template <typename T>
size_t MatrixBase<T>::rows() const {
  return dim_.first;
}

template <Allocator A, typename T>
class Matrix : public MatrixBase<T> {};

template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
    public:
        T &at(size_t i, size_t j);
    private:
        std::vector<T> data_;
};

template <typename T>
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
  return data_[i * this->rows() + j];
}

template <typename T>
class Matrix<Allocator::unique_pointer, T> : public MatrixBase<T> {
    public:
        T &at(size_t i, size_t j);
    private:
        std::unique_ptr<T[]> data_;
};

template <typename T>
T& Matrix<Allocator::unique_pointer, T>::at(size_t i, size_t j) {
  return data_[i * this->rows() + j];
}

First of all, if You override a method You have to declare it in derived class. Secondly, the rows() function could not be resolved by default as a member.

bartop
  • 9,971
  • 1
  • 23
  • 54
  • Why is `this->` required? https://stackoverflow.com/questions/4523545/c-accessing-parent-methods-and-variables says that it is only necessary if ambiguity exists but I don't see any naming conflict – bodokaiser Jul 05 '18 at 13:32
  • 1
    @bodokaiser Check out this one: https://stackoverflow.com/questions/4643074/why-do-i-have-to-access-template-base-class-members-through-the-this-pointer – bartop Jul 05 '18 at 13:35
  • You don't have to declare an override method in your derived class. That is wrong. My answer showed that. – CS Pei Jul 05 '18 at 14:45