2016年4月28日木曜日

C++14による正方行列クラスの作成(その4)

前回は要素にアクセスするための演算子と算術単項演算子を定義した。
今回は除算以外の算術二項演算子と論理演算子を実装してみる。

// matrix.cpp
#include <iostream>
#include <initializer_list>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");


public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix&) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


public:
  //------------------------------------------
  //
  //  要素の参照
  //
  //------------------------------------------
  T& operator ()(int row, int column) {
    return elements_[row][column];
  }

  const T& operator ()(int row, int column) const {
    return elements_[row][column];
  }


public:
  //------------------------------------------
  //
  //  算術単項演算子
  //
  //------------------------------------------
  Matrix operator +() const {
    return Matrix(*this);
  }

  Matrix operator -() const {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        ret.elements_[iRow][iColumn] = -elements_[iRow][iColumn];
      }
    }
    return ret;
  }


public:
  //------------------------------------------
  //
  //  算術代入演算子
  //
  //------------------------------------------
  Matrix& operator +=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] += other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator -=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] -= other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator *=(const T& a) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] *= a;
      }
    }
    return *this;
  }

  Matrix& operator *=(const Matrix& other) & {
    Matrix tmp = *this * other;
    using std::swap;
    swap(*this, tmp);
    return *this;
  }

  Matrix&& operator +=(const Matrix& other) && = delete;
  Matrix&& operator -=(const Matrix& other) && = delete;
  Matrix&& operator *=(const T& a) && = delete;
  Matrix&& operator *=(const Matrix& other) && = delete;


public:
  //------------------------------------------
  //
  //  算術二項演算子
  //
  //------------------------------------------
  friend Matrix operator +(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret += rhs;
  }

  friend Matrix operator -(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret -= rhs;
  }

  friend Matrix operator *(const Matrix& lhs, const T& rhs) {
    Matrix ret(lhs);
    return ret *= rhs;
  }

  friend Matrix operator *(const T& lhs, const Matrix& rhs) {
    return rhs *= lhs;
  }

  friend Matrix operator *(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        for (int i = 0; i < N; i++) {
          ret.elements_[iRow][iColumn] += lhs.elements_[iRow][i] * rhs.elements_[i][iColumn];
        }
      }
    }
    return ret;
  }


public:
  //------------------------------------------
  //
  //  論理演算子
  //
  //------------------------------------------
  friend bool operator ==(const Matrix& lhs, const Matrix& rhs) {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        if (lhs.elements_[iRow][iColumn] != rhs.elements_[iRow][iColumn])
          return false;
      }
    }
    return true;
  }

  friend bool operator !=(const Matrix& lhs, const Matrix& rhs) {
    return !(lhs == rhs);
  }


private:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};
  Matrix<int, 3> y = x;

  y += x;

  std::cout << y << std::endl;
  std::cout << std::boolalpha << (x == (y - x)) << std::endl;

  return 0;
}


実行結果は

[[2,4,6],[8,10,12],[14,16,18]]
true

のようになる。

operator+=()を使って、operator+()を定義するよくある実装を使った。
次回はいよいよ、余因子行列と行列式を定義してみたい。

2016年4月17日日曜日

C++14による正方行列クラスの作成(その3)

前回はコンストラクタとデストラクタ、コピーとムーブのコンストラクタと演算子を定義した。
今回は、要素にアクセスするための関数呼び出し演算子と算術単項演算子を実装してみる。

// matrix.cpp
#include <iostream>
#include <initializer_list>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");


public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix&) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


public:
  //------------------------------------------
  //
  //  要素の参照
  //
  //------------------------------------------
  T& operator ()(int row, int column) {
    return elements_[row][column];
  }

  const T& operator ()(int row, int column) const {
    return elements_[row][column];
  }


public:
  //------------------------------------------
  //
  //  算術単項演算子
  //
  //------------------------------------------
  Matrix operator +() const {
    return Matrix(*this);
  }

  Matrix operator -() const {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        ret.elements_[iRow][iColumn] = -elements_[iRow][iColumn];
      }
    }
    return ret;
  }


protected:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  const Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};

  //x(1, 2) = 13;
  std::cout << x(1, 2) << std::endl;

  std::cout << -x << std::endl;

  return 0;
}


実行結果は

6
[[-1,-2,-3],[-4,-5,-6],[-7,-8,-9]]

のようになる。


次回は、算術二項演算子、論理演算子を定義してみたい。

2016年4月11日月曜日

C++14による正方行列クラスの作成(その2)

前回は、Matrixクラスの挿入演算子を定義し、表示できるようにした。 今回はコンストラクタとデストラクタ、コピーとムーブのコンストラクタと演算子を定義してみる。

// matrix.cpp
#include <iostream>
#include <initializer_list>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");

public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix& other) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


protected:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};
  Matrix<int, 3> y;

  y = x;

  std::cout << x << std::endl;
  std::cout << y << std::endl;

  //Matrix<int, 3>() = x;
  //Matrix<int, 3>() = Matrix<int, 3>();

  return 0;
}


実行結果は

[[1,2,3],[4,5,6],[7,8,9]]
[[1,2,3],[4,5,6],[7,8,9]]

のようになる。


Matrix(const T& a)で、要素型から行列に容易に変換できるようにした。

initializer_listを使って初期化ができるようにした。

また、一時オブジェクトへの代入を禁止した(65-66行目)。


次回は、要素にアクセスするためのoperator()と算術単項演算子を定義してみたい。

2016年4月7日木曜日

C++14による正方行列クラスの作成(その1)

C++11,14の機能を使って、正方行列のクラスを定義してみようと思う。
最終的には行列式を求めることや、余因子行列や逆行列を求める関数も作成したい。
constexprは使わないことにし、pImpl実装もムーブの処理が複雑になるため、とりあえず扱わないことにする。 今回は出力のための挿入演算子を定義してみる。

// matrix.cpp
#include <iostream>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");

public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


protected:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  Matrix<int, 3> x;

  std::cout << x << std::endl;

  return 0;
}


これを

g++ -std=c++14 matrix.cpp

のようにコンパイルすると、実行結果は

[[0,0,0],[0,0,0],[0,0,0]]

のようになる。


static_assertにより、Nが正かどうかをコンパイル時に確認する。

作成した行列クラスはクラステンプレートなので、挿入演算子(operator<<)は friend関数の生成イディオムを使うと定義しやすい。

また、C++11以降であれば、メンバ変数を elements_[N][N] = {} として初期化できる。


次回は、コンストラクタ、デストラクタ、そしてコピーとムーブのコンストラクタと代入演算子を定義してみたい。