C++로 행렬 클래스 만들기 (1)
C++로 행렬 클래스를 만듭니다.
C++20에서는 contexpr 함수에서 중요한 부품으로 사용할 수 있고
<memory_resource>
도 헤더에 잘 대응해야 한다std::pmr::polymorphic_allocator
.<memory_resource>
VC++만 보고 대응하기 때문에 VC++로 C++ 버전을 C+20으로 제작합니다.매우 길기 때문에 문장을 분할해야 한다.
matrix_type
matrix_type
개념으로 행렬에서 사용하고 싶은 유형이 충족되기를 원하는 요구를 정의한다.어쨌든 사칙연산과 대입만 가능하면 OK.
#include <concepts>
template<typename T>
concept matrix_type = requires(T& a, T& b){
{ a + b } -> std::convertible_to<T>;
{ a - b } -> std::convertible_to<T>;
{ a * b } -> std::convertible_to<T>;
{ a / b } -> std::convertible_to<T>;
a = b;
a = std::move(b);
};
static_assert(matrix_type<int>);
static_assert(matrix_type<float>);
static_assert(matrix_type<double>);
필요에 따라 추가 요건.dynamic_matrix
희망 요소 수가 가변적
dynamic_matrix
으로 바뀌었기 때문에 이 이름을 지었다.#include <memory>
#include <cassert>
template<matrix_type T, typename Alloc=std::allocator<T>>
class dynamic_matrix{
public:
using allocator_type = std::allocator_traits<Alloc>::template rebind_alloc<T>;
using value_type = std::allocator_traits<allocator_type>::value_type;
using pointer = std::allocator_traits<allocator_type>::pointer;
using const_pointer = std::allocator_traits<allocator_type>::const_pointer;
using reference = value_type &;
using const_reference = const value_type &;
using size_type = std::allocator_traits<allocator_type>::size_type;
using index_type = size_type;
//メモリアクセス
constexpr reference operator()(index_type i, index_type j) &noexcept{
assert(m_elem != nullptr);
assert(i < m_rows);
assert(j < m_cols);
return m_elem[m_colcap * i + j];
}
constexpr const_reference operator()(index_type i, index_type j) const &noexcept{
assert(m_elem != nullptr);
assert(i < m_rows);
assert(j < m_cols);
return m_elem[m_colcap * i + j];
}
constexpr value_type operator()(index_type i, index_type j) const &&noexcept{
assert(m_elem != nullptr);
assert(i < m_rows);
assert(j < m_cols);
return m_elem[m_colcap * i + j];
}
//実際に使うサイズ
constexpr size_type rows()const noexcept {
return m_rows;
}
constexpr size_type columns()const noexcept {
return m_cols;
}
constexpr size_type size()const noexcept {
return rows() * columns();
}
//余分に確保しているメモリサイズ
constexpr size_type row_capacity()const noexcept {
return m_rowcap;
}
constexpr size_type column_capacity()const noexcept {
return m_colcap;
}
constexpr size_type capacity()const noexcept {
return row_capacity() * column_capacity();
}
//アロケータを返す
constexpr allocator_type get_allocator() const noexcept {
return m_alloc;
}
private:
using traits = std::allocator_traits<allocator_type>;
//m_elemがnullptrであるときallocatorされていない、
//もしくはdeallocate済みで有ることを保証すること。
//関数から抜けたときにm_elemがnullptr以外であれば
//allocateされているという事も保証する事
//constexpr対応の為に必須の要件とする。
pointer m_elem = nullptr;
allocator_type m_alloc = {};
size_type m_rows = 0;
size_type m_cols = 0;
size_type m_rowcap = 0;
size_type m_colcap = 0;
};
dynamic_matrix
의 메모리는 다음과 같이 예정되어 있습니다.figure1-1: dynamic_메모리 이미지
상기 회색 부분은 불필요하게 확보된 메모리
***_capacity()
이고 디지털 부분은 실제 행렬로 사용되는 범위이다.회색 부분에 접근하는 값이 정의되지 않았습니다.
행 색인
i
, 열 색인j
시 M{ij}에 접근하고 싶은 경우 다음과 같은 식이 됩니다.m_elem[m_colcap * i + j]
수학 행렬과 달리 색인은 0입니다예를 들어, 위 매트릭스
i=1, j=2
는 6
입니다.메모리 액세스
메모리 확보
상기 내용으로 메모리를 확보하는 함수를 제작한다.
함수 이름 참조
std::vector
.public:
//dynamic_matrixのメンバー関数
//メモリを確保する。現在のメモリより小さくはしない。
constexpr void reserve(
size_type rows, size_type cols,
size_type rowcap, size_type colcap){
//rowcap,colcapはそれぞれrows,colsより小さくてはいけない
if(rows > rowcap)
rowcap = rows;
if(cols > colcap)
colcap = cols;
//現在のcapacityより大きくメモリを確保したいときのみメモリ確保を行う
if (row_capacity() < rowcap || column_capacity() < colcap) {
//一時的なメモリを確保
pointer tmp = traits::allocate(m_alloc, rowcap * colcap);
//constexpr関数内で使う為にメモリ全て初期化する。
for (pointer sptr = tmp, eptr = tmp + rowcap * colcap; sptr < eptr; ++sptr)
traits::construct(m_alloc, sptr);
//すでにm_elemでメモリを確保されている場合にのみtmpにデータをコピー
if (m_elem) {
size_type dst_rows = std::min(rows, m_rows);
size_type dst_cols = std::min(cols, m_cols);
for (size_type ri = 0; ri < dst_rows; ++ri)
for (size_type ci = 0; ci < dst_cols; ++ci)
tmp[colcap * ri + ci] = (*this)(ri, ci);
traits::deallocate(m_alloc, m_elem, capacity());
}
m_elem = tmp;
m_rowcap = rowcap;
m_colcap = colcap;
}
m_rows = rows;
m_cols = cols;
}
//容量のみの変更を想定
constexpr void reserve(size_type rowcap, size_type colcap){
reserve(m_rows, m_cols, rowcap, colcap);
}
//サイズの変更を想定。
//ただし現在の容量を超えるサイズであればメモリを再確保する。
constexpr void resize(size_type rows, size_type cols){
reserve(rows, cols, m_rowcap, m_colcap);
}
//rows()*columns()の範囲を指定の値で初期化
constexpr void fill(const T& value) {
for (size_type ri = 0; ri < m_rows; ++ri)
for (size_type ci = 0; ci < m_cols; ++ci)
(*this)(ri, ci) = value;
}
//デストラクタもconstexprとする事。
constexpr ~dynamic_matrix(){
deallocate();
}
private:
constexpr void deallocate(bool post_processing=true){
//重要: m_elemがnullptrの場合、allocateされていないか、
//すでにdeallocateされているものとする。
if (m_elem) {
//後処理
for (pointer sptr = m_elem, eptr = m_elem + capacity(); sptr < eptr; ++sptr)
traits::destroy(m_alloc, sptr);
//メモリ解放
traits::deallocate(m_alloc, m_elem, capacity());
m_elem = nullptr;
}
//後始末をしないほうがいいときがあるのでpost_processingで分岐する。
//基本は後始末をしたほうがいいのでデフォルトではtrueである。
if(post_processing){
m_elem = nullptr;
m_rows = 0;
m_cols = 0;
m_rowcap = 0;
m_colcap = 0;
}
}
초기화(std:initializer list)
메모리를 확보할 수 있기 때문에 구조기로 초기화하고 싶습니다.
public:
//dynamic_matrixのコンストラクタ
//std::initializer_list<U>のサイズは全て同じである事を条件とする。
//異なる場合未定義とする
template<std::convertible_to<T> U>
constexpr dynamic_matrix(std::initializer_list<std::initializer_list<U>> mat,
const allocator_type &alloc = allocator_type()):
//allocatorに代入演算子が存在しない事もあるので初期化はここでのみ。
m_alloc(alloc){
size_type M = mat.size();
size_type N = mat.begin()->size();
resize(M, N);
index_type ri = 0;
for (auto &&row : mat) {
index_type ci = 0;
for (auto &&value : row)
(*this)(ri,ci++) = static_cast<T>(value);
++ri;
}
}
상기 구조기는 다음과 같이 초기화할 수 있다.dynamic_matrix<double> mat({
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
});
출력
초기화할 수 있다면 출력도 이해하기 쉽다.
#include <iostream>
#include <iomanip>
template <typename Char, matrix_type T, typename Alloc>
std::basic_ostream<Char> &operator<<(
std::basic_ostream<Char> &os, const dynamic_matrix<T, Alloc> &mat) {
using size_type = dynamic_matrix<T, Alloc>::size_type;
size_type M = mat.rows();
size_type N = mat.columns();
os << std::showpos << std::fixed << std::setprecision(3);
for (size_type ri = 0; ri < M; ++ri) {
for (size_type ci = 0; ci < N - 1; ++ci)
os << std::setw(10) << mat(ri, ci) << Char(',');
os << std::setw(10) << mat(ri, N - 1) << Char(',') << std::endl;
}
return os;
}
다음 코드를 사용합니다int main() {
dynamic_matrix<double> mat({
{1, 2, 3}, //
{4, 5, 6}, //
{7, 8, 9} //
});
std::cout << mat << std::endl;
}
출력은 다음과 같다. +1.000, +2.000, +3.000,
+4.000, +5.000, +6.000,
+7.000, +8.000, +9.000,
일단 총결산하다
이상은 길어지기 때문에 아래 기사에서.
학급은 아래와 같다.
#include <concepts>
#include <memory>
#include <cassert>
#include <iostream>
#include <iomanip>
template <typename T>
concept matrix_type = requires(T &a, T &b) {
{ a + b } -> std::convertible_to<T>;
{ a - b } -> std::convertible_to<T>;
{ a * b } -> std::convertible_to<T>;
{ a / b } -> std::convertible_to<T>;
a = b;
a = std::move(b);
};
static_assert(matrix_type<int>);
static_assert(matrix_type<float>);
static_assert(matrix_type<double>);
template <matrix_type T, typename Alloc = std::allocator<T>>
class dynamic_matrix {
public:
using allocator_type = std::allocator_traits<Alloc>::template rebind_alloc<T>;
using value_type = std::allocator_traits<allocator_type>::value_type;
using pointer = std::allocator_traits<allocator_type>::pointer;
using const_pointer = std::allocator_traits<allocator_type>::const_pointer;
using reference = value_type &;
using const_reference = const value_type &;
using size_type = std::allocator_traits<allocator_type>::size_type;
using index_type = size_type;
//dynamic_matrixのコンストラクタ
template <std::convertible_to<T> U>
constexpr dynamic_matrix(std::initializer_list<std::initializer_list<U>> mat,
const allocator_type &alloc = allocator_type())
: //allocatorに代入演算子が存在しない事もあるので初期化はここでのみ。
m_alloc(alloc) {
size_type M = mat.size();
size_type N = mat.begin()->size();
resize(M, N);
index_type ri = 0;
for (auto &&row : mat) {
index_type ci = 0;
for (auto &&value : row)
(*this)(ri, ci++) = static_cast<T>(value);
++ri;
}
}
//メモリアクセス
constexpr reference operator()(index_type i, index_type j) &noexcept {
assert(m_elem != nullptr);
assert(i < m_rows);
assert(j < m_cols);
return m_elem[m_colcap * i + j];
}
constexpr const_reference operator()(index_type i, index_type j) const &noexcept {
assert(m_elem != nullptr);
assert(i < m_rows);
assert(j < m_cols);
return m_elem[m_colcap * i + j];
}
constexpr value_type operator()(index_type i, index_type j) const &&noexcept {
assert(m_elem != nullptr);
assert(i < m_rows);
assert(j < m_cols);
return m_elem[m_colcap * i + j];
}
//実際に使うサイズ
constexpr size_type rows() const noexcept {
return m_rows;
}
constexpr size_type columns() const noexcept {
return m_cols;
}
constexpr size_type size() const noexcept {
return rows() * columns();
}
//余分に確保しているメモリサイズ
constexpr size_type row_capacity() const noexcept {
return m_rowcap;
}
constexpr size_type column_capacity() const noexcept {
return m_colcap;
}
constexpr size_type capacity() const noexcept {
return row_capacity() * column_capacity();
}
//アロケータを返す
constexpr allocator_type get_allocator() const noexcept {
return m_alloc;
}
//メモリ確保
constexpr void reserve(
size_type rows, size_type cols, size_type rowcap, size_type colcap) {
//rowcap,colcapはそれぞれrows,colsより小さくてはいけない
if (rows > rowcap)
rowcap = rows;
if (cols > colcap)
colcap = cols;
//現在のcapacityより大きくメモリを確保したいときのみメモリ確保を行う
if (row_capacity() < rowcap || column_capacity() < colcap) {
//一時的なメモリを確保
pointer tmp = traits::allocate(m_alloc, rowcap * colcap);
//constexpr関数内で使う為にメモリ全て初期化する。
for (pointer sptr = tmp, eptr = tmp + rowcap * colcap; sptr < eptr; ++sptr)
traits::construct(m_alloc, sptr);
//すでにm_elemでメモリを確保されている場合にのみtmpにデータをコピー
if (m_elem) {
size_type dst_rows = std::min(rows, m_rows);
size_type dst_cols = std::min(cols, m_cols);
for (size_type ri = 0; ri < dst_rows; ++ri)
for (size_type ci = 0; ci < dst_cols; ++ci)
tmp[colcap * ri + ci] = (*this)(ri, ci);
traits::deallocate(m_alloc, m_elem, capacity());
}
m_elem = tmp;
m_rowcap = rowcap;
m_colcap = colcap;
}
m_rows = rows;
m_cols = cols;
}
//容量のみの変更を想定
constexpr void reserve(size_type rowcap, size_type colcap) {
reserve(m_rows, m_cols, rowcap, colcap);
}
//サイズの変更を想定。
//ただし現在の容量を超えるサイズであればメモリを再確保する。
constexpr void resize(size_type rows, size_type cols) {
reserve(rows, cols, m_rowcap, m_colcap);
}
//デストラクタもconstexprとする事。
constexpr ~dynamic_matrix() {
deallocate();
}
//rows()*columns()の範囲を指定の値で初期化
constexpr void fill(const T& value) {
for (size_type ri = 0; ri < m_rows; ++ri)
for (size_type ci = 0; ci < m_cols; ++ci)
(*this)(ri, ci) = value;
}
private:
constexpr void deallocate(bool post_processing = true) {
//重要: m_elemがnullptrの場合、allocateされていないか、
//すでにdeallocateされているものとする。
if (m_elem) {
//後処理
for (pointer sptr = m_elem, eptr = m_elem + capacity(); sptr < eptr; ++sptr)
traits::destroy(m_alloc, sptr);
//メモリ解放
traits::deallocate(m_alloc, m_elem, capacity());
m_elem = nullptr;
}
//後始末をしないほうがいいときがあるのでpost_processingで分岐する。
//基本は後始末をしたほうがいいのでデフォルトではtrueである。
if (post_processing) {
m_elem = nullptr;
m_rows = 0;
m_cols = 0;
m_rowcap = 0;
m_colcap = 0;
}
}
private:
using traits = std::allocator_traits<allocator_type>;
//m_elemがnullptrであるときallocatorされていない、
//もしくはdeallocate済みで有ることを保証すること。
//関数から抜けたときにm_elemがnullptr以外であれば
//allocateされているという事も保証する事
//constexpr対応の為に必須の要件とする。
pointer m_elem = nullptr;
allocator_type m_alloc = {};
size_type m_rows = 0;
size_type m_cols = 0;
size_type m_rowcap = 0;
size_type m_colcap = 0;
};
template <typename Char, matrix_type T, typename Alloc>
std::basic_ostream<Char> &operator<<(
std::basic_ostream<Char> &os, const dynamic_matrix<T, Alloc> &mat) {
using size_type = dynamic_matrix<T, Alloc>::size_type;
size_type M = mat.rows();
size_type N = mat.columns();
os << std::showpos << std::fixed << std::setprecision(3);
for (size_type ri = 0; ri < M; ++ri) {
for (size_type ci = 0; ci < N - 1; ++ci)
os << std::setw(10) << mat(ri, ci) << Char(',');
os << std::setw(10) << mat(ri, N - 1) << Char(',') << std::endl;
}
return os;
}
보충현재contexpr 함수에서 사용할 수 있습니다.
constexpr double test() {
dynamic_matrix<double> mat({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
return mat(1, 1);
}
int main(){
constexpr auto res = test();//res: 5
//以下のようなconstexpr変数はC++20時点でも不可である。
//constexpr dynamic_matrix<double> mat1({{1,2,3},{4,5,6}});
}
마지막으로 다음 창고를 참고하세요.Reference
이 문제에 관하여(C++로 행렬 클래스 만들기 (1)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/yhsi/articles/cae6012aaf5a39텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)