C++ 有没有一种方法可以在C++;11构造二维矩阵?
假设您有一个简单的矩阵类C++ 有没有一种方法可以在C++;11构造二维矩阵?,c++,c++11,nested-lists,initializer-list,C++,C++11,Nested Lists,Initializer List,假设您有一个简单的矩阵类 template <typename T = double> class Matrix { T* data; size_t row, col; public: Matrix(size_t m, size_t n) : row(m), col(n), data(new T[m*n]) {} //... friend std::ostream& operator<<(std::ostream&
template <typename T = double>
class Matrix {
T* data;
size_t row, col;
public:
Matrix(size_t m, size_t n) : row(m), col(n), data(new T[m*n]) {}
//...
friend std::ostream& operator<<(std::ostream& os, const Matrix& m) {
for (int i=0; i<m.row; ++i) {
for (int j=0; j<m.col; ++j)
os<<" "<<m.data[i + j*m.row];
os<<endl;
}
return os;
}
};
将打印
1 3 4
2 6 2
期待你的回答。谢谢大家。
aa
编辑
所以我根据你的建议设计了一个有点通用的数组,它使用初始化器列表初始化元素。但这是我能得到的最通用的。
如果你们有任何人对如何使它成为一个更通用的类有任何建议,我将不胜感激。
还有几个问题:
- 派生类初始化基类的状态可以吗?我不是因为这个而调用基构造函数,但是我应该调用它吗
- 我将析构函数a和泛型_基类定义为受保护的,这是正确的方法吗
- 是否有任何可预见的方法以更通用的方式执行属于初始值设定项构造函数的代码?我的意思是要有一个能处理所有情况的通用构造函数
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
template <int d, typename T>
class Generic_base {
protected:
typedef T value_type;
Generic_base() : n_(), data_(nullptr){}
size_t n_[d] = {0};
value_type* data_;
};
template <int d, typename T>
class Generic_traits;
template <typename T>
class Generic_traits<1,T> : public Generic_base<1,T> {
protected:
typedef T value_type;
typedef Generic_base<1,T> base_type;
typedef std::initializer_list<T> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
data_ = new T[n_[0]];
int i = 0;
for (const auto& v : l)
data_[i++] = v;
}
};
template <typename T>
class Generic_traits<2,T> : public Generic_base<2,T> {
protected:
typedef T value_type;
typedef Generic_base<2,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
n_[1] = l.begin()->size();
data_ = new T[n_[0]*n_[1]];
int i = 0, j = 0;
for (const auto& r : l) {
assert(r.size() == n_[1]);
for (const auto& v : r) {
data_[i + j*n_[0]] = v;
++j;
}
j = 0;
++i;
}
}
};
template <typename T>
class Generic_traits<4,T> : public Generic_base<4,T> {
protected:
typedef T value_type;
typedef Generic_base<4,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> llist_type;
typedef std::initializer_list<llist_type> lllist_type;
typedef std::initializer_list<lllist_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
assert(l.begin()->size() > 0);
assert(l.begin()->begin()->size() > 0);
assert(l.begin()->begin()->begin()->size() > 0);
size_t m = n_[0] = l.size();
size_t n = n_[1] = l.begin()->size();
size_t o = n_[2] = l.begin()->begin()->size();
n_[3] = l.begin()->begin()->begin()->size();
data_ = new T[m*n*o*n_[3]];
int i=0, j=0, k=0, p=0;
for (const auto& u : l) {
assert(u.size() == n_[1]);
for (const auto& v : u) {
assert(v.size() == n_[2]);
for (const auto& x : v) {
assert(x.size() == n_[3]);
for (const auto& y : x) {
data_[i + m*j + m*n*k + m*n*o*p] = y;
++p;
}
p = 0;
++k;
}
k = 0;
++j;
}
j = 0;
++i;
}
}
};
template <int d, typename T>
class Generic : public Generic_traits<d,T> {
public:
typedef Generic_traits<d,T> traits_type;
typedef typename traits_type::base_type base_type;
using base_type::n_;
using base_type::data_;
typedef typename traits_type::initializer_type initializer_type;
// initializer list constructor
Generic(initializer_type l) : traits_type(l) {}
size_t size() const {
size_t n = 1;
for (size_t i=0; i<d; ++i)
n *= n_[i];
return n;
}
friend std::ostream& operator<<(std::ostream& os, const Generic& a) {
for (int i=0; i<a.size(); ++i)
os<<" "<<a.data_[i];
return os<<endl;
}
};
int main()
{
// constructors for initializer lists
Generic<1, double> y = { 1., 2., 3., 4.};
cout<<"y -> "<<y<<endl;
Generic<2, double> C = { {1., 2., 3.}, {4., 5., 6.} };
cout<<"C -> "<<C<<endl;
Generic<4, double> TT = { {{{1.}, {7.}, {13.}, {19}}, {{2}, {8}, {14}, {20}}, {{3}, {9}, {15}, {21}}}, {{{4.}, {10}, {16}, {22}}, {{5}, {11}, {17}, {23}}, {{6}, {12}, {18}, {24}}} };
cout<<"TT -> "<<TT<<endl;
return 0;
}
为什么不呢
Matrix(std::initializer_list<std::initializer_list<T>> lst) :
Matrix(lst.size(), lst.size() ? lst.begin()->size() : 0)
{
int i = 0, j = 0;
for (const auto& l : lst)
{
for (const auto& v : l)
{
data[i + j * row] = v;
++j;
}
j = 0;
++i;
}
}
矩阵(标准::初始值设定项列表lst):
矩阵(lst.size(),lst.size()?lst.begin()->size():0)
{
int i=0,j=0;
用于(常数自动和l:lst)
{
用于(常数自动和v:l)
{
数据[i+j*行]=v;
++j;
}
j=0;
++一,;
}
}
正如stardust所建议的,这里应该使用向量,而不是数组。使用初始值设定项列表来解决这个问题的主要问题是,它们的大小在编译时不容易访问。看起来这个特殊的类是用于动态矩阵的,但是如果您想在堆栈上这样做(通常是出于速度/局部性原因),下面是一个提示,说明您需要什么(C++17): 它将在编译时初始化类型为的对象:
const matrix<int, 4, 3>
常数矩阵
如果您的编译器支持C++20,我建议您在CTAD中添加一个“requires”子句,以确保所有子数组的大小相同(从数学上讲,n1==n2==n3==n4,等等)。他可能也应该使用向量而不是数组来表示数据。您应该注意(对于额外的点数断言)
lst
不为空且每行大小相同的前提条件。通过引用传递初始值设定项列表没有意义。这个值更合适。初始值设定项\u列表
不拥有或深度复制其内容。@stardust\u为什么是向量?向量也只是数组的包装器,对吗?@AlejandroMarcosAragon我想这就解释了。如前所述,没有什么比使用std::initializer\u list
的构造函数更令人费解的了。但它只适用于声明中的初始化,而不适用于赋值表达式,因为运算符不适用于带大括号的init列表。而且编译器对大括号init列表的支持仍然存在广泛的缺陷,因此您可能需要升级或尝试其他平台。
Matrix(std::initializer_list<std::initializer_list<T>> lst) :
Matrix(lst.size(), lst.size() ? lst.begin()->size() : 0)
{
int i = 0, j = 0;
for (const auto& l : lst)
{
for (const auto& v : l)
{
data[i + j * row] = v;
++j;
}
j = 0;
++i;
}
}
template<typename elem_t, size_t ... dim>
struct matrix
{
template<size_t ... n>
constexpr matrix(const elem_t (&...list)[n]) : data{}
{
auto pos = &data[0];
((pos = std::copy(list, list + n, pos)), ...);
}
int data[dim...];
};
template<typename ... elem_t, size_t ... n>
matrix(const elem_t (&...list)[n]) -> matrix<std::common_type_t<elem_t...>, sizeof...(n), (n * ...) / sizeof...(n)>;
constexpr matrix mat{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} };
const matrix<int, 4, 3>