Usually array could be initialized like this:
int ooo[3][3] =
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};
I need to hide it into the custom structure like this:
template <typename T, size_t dim1, size_t dim2>
struct MyStruct
{
private:
T m_data[dim1][dim2];
};
How to pass the initialization to the array like in the foolowing case?
MyStruct<int, 3, 3> s =
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};
It would work if you can make the member public, and add a set of braces. This is aggregate initialisation:
MyStruct<int, 3, 3> s =
{{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
}};
I need to hide it into the custom structure ...
If you need the member to be private, you cannot rely on aggregate initialisation since the class won't be an aggregate. You can still achieve similar syntax if you define a custom constructor. Here is an example using std::initializer_list. This one doesn't require the extra braces.
Edit: This isn't as good as the array reference example in the other answer.
MyStruct(std::initializer_list<std::initializer_list<T>> ll)
{
auto row_in = ll.begin();
for (auto& row : m_data)
{
auto col_in = (row_in++)->begin();
for (auto& i : row)
{
i = *col_in++;
}
}
}
You can add a constructor to your class that takes a 2D array argument, and copies it into the array member:
template <typename T, size_t dim1, size_t dim2>
struct MyStruct
{
private:
T m_data[dim1][dim2];
public:
MyStruct(T const (&a)[dim1][dim2]) {
for (int i = 0; i < dim1; ++i)
for (int j = 0; j < dim2; ++j)
m_data[i][j] = a[i][j];
}
};
You also need to add an extra pair of braces at the call site.
Here's a demo.
Related
I have this piece of code in C++:
std::array<std::array<int, 2>, 4> elements = {{1,2},{1,2},{1,2},{1,2}};
and it says: error: excess elements in struct initializer
I don't know why that happens. If I write that it works:
std::array<std::array<int, 2>, 4> elements = {{1,2}};
To initialize an std::array object you need two curly-brace pairs: The outer curly-braces is for the object itself, and the inner is for the array.
That means your initialization needs to look like
std::array<std::array<int, 2>, 4> elements = {{{1,2},{1,2},{1,2},{1,2}}};
std::array is subtle in aggregate initialization; it expects on more braces for the subobject (underlying array).
std::array<std::array<int, 2>, 4> elements = {{{1,2},{1,2},{1,2},{1,2}}};
// ^ ^ <- for std::array
// ^ ^ <- for underlying array
You got the error for the initializer {{1,2},{1,2},{1,2},{1,2}} because there're 4 {1,2}s to initialize the subobject (underlying array) but std::array has only one, i.e. excess elements.
Here are some examples how you can initialize an object of the type you are using.
#include <iostream>
#include <array>
int main()
{
std::array<std::array<int, 2>, 4> elements1 = { 1, 2, 1, 2, 1, 2, 1, 2 };
std::array<std::array<int, 2>, 4> elements2 = { { 1, 2, 1, 2, 1, 2, 1, 2 } };
std::array<std::array<int, 2>, 4> elements3 =
{
{
{ 1, 2 }, { 1, 2 }, { 1, 2 }, { 1, 2 }
}
};
std::array<std::array<int, 2>, 4> elements4 =
{
{
{ { 1, 2 } }, { { 1, 2 } }, { { 1, 2 } }, { { 1, 2 } }
}
};
std::array<std::array<int, 2>, 4> elements5 =
{
{
{ 1, 2 }, 1, 2, 1, 2, { 1, 2 }
}
};
return 0;
}
Pay attention to the following quote from the C++ 17 Standard (11.6.1 Aggregates)
12 Braces can be elided in an initializer-list as follows. If the
initializer-list begins with a left brace, then the succeeding
comma-separated list of initializer-clauses initializes the elements
of a subaggregate; it is erroneous for there to be more
initializer-clauses than elements. If, however, the initializer-list
for a subaggregate does not begin with a left brace, then only enough
initializer-clauses from the list are taken to initialize the elements
of the subaggregate; any remaining initializer-clauses are left to
initialize the next element of the aggregate of which the current
subaggregate is an element.
std::array is defined as an aggregate that contains another aggregate.
Here is a demonstrative program to make the quote more clear that shows an initialization of a two-dimensional array
#include <iostream>
int main()
{
const size_t M = 4, N = 4;
int a[M][N] =
{
{ 1, 2, 3, 4 },
5, 6, 7, 8,
{ 8, 7, 6, 5 },
4, 3, 2, 1,
};
return 0;
}
You may imagine the representation of the type std::array<std::array<int, 2>, 4 > as a declaration of the structure B in the following demonstrative porgram.
#include <iostream>
struct A
{
int a[2];
};
struct B
{
A b[4];
};
int main()
{
B b1 =
{
};
B b2 =
{
{
}
};
B b3 =
{
{
{
}
}
};
B b4 =
{
{
{
{ {}, {} }
}
}
};
return 0;
}
Investigate the program.
I am trying to use std:copy to copy the 3D array to vector.
int myint[3][3][3] = { { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } },
{ { 11,12, 13 }, { 14, 15, 16 }, {17, 18, 19 } },
{ { 21, 22, 23 }, { 24, 25, 26 }, { 27, 28, 29 } } };
int favint[3][3][3];
I can copy the myint to favint as below
std::copy(&myint[0][0][0],
&myint[0][0][0] + (sizeof(myint) / sizeof(*myint)) * (sizeof(*myint) / sizeof(**myint))*(sizeof(**myint) / sizeof(***myint)),
&favint[0][0][0]);
I can copy the myint to myvector as below:
vector<vector<vector<int> > > myvector;
for (int k = 0; k<sizeof(myint) / sizeof(*myint); k++)
{
myvector.push_back(vector<vector<int> >());
for (int i = 0; i<sizeof(*myint) / sizeof(**myint); i++)
{
myvector[k].push_back(vector<int>());
for (int j = 0; j<sizeof(**myint) / sizeof(***myint); j++)
{
myvector[k][i].push_back(myint[k][i][j]);
}
}
}
Copy 3D array to a 3D array is simple using std:: copy. How can I use the std::copy to copy the myint to myvector? Is there any other simple way?
You might do something like:
std::vector<int> to_vector(const int (&a)[3])
{
return {std::begin(a), std::end(a)};
}
std::vector<std::vector<int>> to_vector(const int (&a)[3][3])
{
return {to_vector(a[0]), to_vector(a[1]), to_vector(a[2])};
}
std::vector<std::vector<std::vector<int>>> to_vector(const int (&a)[3][3][3])
{
return {to_vector(a[0]), to_vector(a[1]), to_vector(a[2])};
}
For generic (but compile time) values, template might help:
template <std::size_t N>
std::vector<int> to_vector(const int (&a)[N])
{
return {std::begin(a), std::end(a)};
}
template <std::size_t N1, std::size_t N2>
auto to_vector(const int (&a)[N1][N2])
-> std::vector<std::vector<int>>
{
std::vector<std::vector<int>> res;
res.reserve(N1);
for (const auto& inner_a : a) {
res.push_back(to_vector(inner_a));
}
return res;
}
template <std::size_t N1, std::size_t N2, std::size_t N3>
auto to_vector(const int (&a)[N1][N2][N3])
-> std::vector<std::vector<std::vector<int>>>
{
std::vector<std::vector<std::vector<int>>> res;
res.reserve(N1);
for (const auto& inner_a : a) {
res.push_back(to_vector(inner_a));
}
return res;
}
Demo
Simply use std::back_inserter
std::copy(&myint[0][0][0],
&myint[0][0][0] + sizeof myint / sizeof(int),
std::back_inserter(myvector));
This question already has answers here:
Zero-initializing an array data member in a constructor
(6 answers)
Closed 5 years ago.
In C this can be done easily by doing
int myArray[10] = { 0 }; // all elements 0
(taken from this answer)
My question is, is there a way similar (or same) in C++ which I can use to initialize the array to 0 in the constructor?
EDIT 1
I need to do the above using initialization list.
May be something like
struct aa
{
int i[10];
aa() : i {0}{}
};
Yes. Same syntax applies in C++, although it has a different name (list initialization, or in this case its special case, aggregate initialization).
However, in the member initialization list, you have to use the different syntax: myArray{}. Note that explicitly initializing the first element to 0 is unnecessary, since that is the default.
Here is an example how it can be done
struct A
{
A() : a{} {}
enum { N = 10 };
int a[N];
} a;
Or
struct A
{
A() : a{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } {}
enum { N = 10 };
int a[N];
} a;
for (int x : a.a) std::cout << x << ' ';
std::cout << std::endl;
Another approach is to use an object of type std::array. For example
#include <array>
//...
struct A
{
A()
{
a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
enum { N = 10 };
std::array<int, N> a;
} a;
for (int x : a.a) std::cout << x << ' ';
std::cout << std::endl;
I am literally freaking out on the following :
template <class T> // int, float, double etc..
class Graph {
public:
// Documentation, it has to be between [0..100]
Graph(int size = 10, int density = 10, T range = 0):
m_size(size),
m_density(density),
m_range(range) {
generate();
}
~Graph() {
for (int i = 0; i < m_size; i++)
delete[] m_graph[i];
delete[] m_graph;
}
[..]
static Graph<T>* custom(T** cgraph, int size, T range) {
Graph<T> *graph = new Graph<T>(size, 10, range);
for (int i = 0; i < size; i++)
delete[] graph->m_graph[i];
delete[] graph->m_graph;
graph->m_graph = cgraph;
}
private:
T** m_graph;
[ .. ]
};
int nodes[4][4] = {
{ 6, 5, 2, 5 },
{ 5, 6, 3, 3 },
{ 1, 3, 6, 1 },
{ 5, 3, 1, 6 }
};
int main() {
Graph<int> *graph = Graph<int>::custom(nodes, 4, 5);
}
What is it poorly failing to compile reporting the following errors ?
g++ graph.cpp -o test_graph
graph.cpp: In function ‘int main()’:
graph.cpp:191:55: error: no matching function for call to ‘Graph<int>::custom(int [4][4], int, int)’
Graph<int> *graph = Graph<int>::custom(nodes, 4, 5);
^
graph.cpp:60:20: note: candidate: static Graph<T>* Graph<T>::custom(T**, int, T) [with T = int]
static Graph<T>* custom(T** cgraph, int size, T range) {
^
graph.cpp:60:20: note: no known conversion for argument 1 from ‘int [4][4]’ to ‘int**’
It looks so right to me, what's wrong ?
You need to make nodes by an array of pointers to int.
int nodes_v[4][4] = {
{ 6, 5, 2, 5 },
{ 5, 6, 3, 3 },
{ 1, 3, 6, 1 },
{ 5, 3, 1, 6 }
};
int *nodes[4] = { nodes_v[0], nodes_v[1], nodes_v[2], nodes_v[3] };
You also need to add an additional member to the Graph variable to mark that it is a custom graph, and if set, the destructor should not delete the memory.
Best to give Graph a private constructor which passes in the custom flag and doesn't bother allocating the memory if set.
Graph(int size, int density, T range, bool custom):
m_size(size),
m_density(density),
m_range(range),
m_custom {
}
Graph(int size = 10, int density = 10, T range = 0):
Graph(size, density, range, false) {
generate();
}
static Graph<T>* custom(T** cgraph, int size, T range) {
Graph<T> *graph = new Graph<T>(size, 10, range, true);
graph->m_graph = cgraph;
}
Finally, you need to handle copy constructor and assignment operator (start by just deleting them).
Imagine you have a simple matrix class
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;
}
};
Is there a way that I can initialize this matrix with an initializer list? I mean to obtain the sizes of the matrix and the elements from an initializer list. Something like the following code:
Matrix m = { {1., 3., 4.}, {2., 6, 2.}};
would print
1 3 4
2 6 2
Looking forward to your answers. Thank you all.
aa
EDIT
So I worked on your suggestions to craft a somewhat generic array that initializes elements using initializer lists. But this is the most generic I could obtain.
I would appreciate if any of you have any suggestions as to make it a more generic class.
Also, a couple of questions:
Is it fine that a derived class initializes the state of the base class? I'm not calling the base constructor because of this, but should I call it anyways?
I defined the destructor a the Generic_base class as protected, is this the right way to do it?
Is there any foreseeable way to carry out the code that belongs to the constructor of the initializer in a more generic way? I mean to have one general constructor that takes care of all cases?
I included just the necessary code to illustrate the use of initializer lists in construction. When going to higher dimensions it gets messy, but I did one just to check the code.
#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;
}
Which prints as expected:
y -> 1 2 3 4
C -> 1 4 2 5 3 6
TT -> 1 4 2 5 3 6 7 10 8 11 9 12 13 16 14 17 15 18 19 22 20 23 21 24
Why not?
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;
}
}
And as stardust_ suggests - you should use vectors, not arrays here.
The main issue with using initializer lists to tackle this problem, is that their size is not easily accessible at compile time. It looks like this particular class is for dynamic matrices, but if you wanted to do this on the stack (usually for speed/locality reasons), here is a hint at what you need (C++17):
template<typename elem_t, std::size_t ... dim>
struct matrix
{
template<std::size_t ... n>
constexpr matrix(const elem_t (&...list)[n]) : data{}
{
auto pos = &data[0];
((pos = std::copy(list, list + n, pos)), ...);
}
elem_t data[(dim * ... * 1)];
};
template<typename ... elem_t, std::size_t ... n>
matrix(const elem_t (&...list)[n]) -> matrix<std::common_type_t<elem_t...>, sizeof...(n), (n * ... * 1) / sizeof...(n)>;
I had to tackle this same problem in my linear algebra library, so I understand how unintuitive this is at first. But if you instead pass a C-array into your constructor, you will have both type and size information of the values you've passed in. Also take note of the constuctor template argument deduction (CTAD) to abstract away the template arguments.
You can then create constexpr matrix objects like this (or, leave out constexpr to simply do this at runtime on the stack):
constexpr matrix mat{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} };
Which will initialize an object at compile time of type:
const matrix<int, 4, 3>
If C++20 is supported by your compiler, I would recommend adding a "requires" clause to the CTAD to ensure that all sub-arrays are the same size (mathematically-speaking, n1 == n2 == n3 == n4, etc).
Using std::vector::emplace_back() (longer)
Using std::vector, instead of plain old array, you can use std::vector::emplace_back() to fill the vector:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row(lst.size())
, col(lst.size() ? lst.begin()->size() : 0) // Minimal validation
{
// Eliminate reallocations as we already know the size of matrix
data.reserve(row * col);
for (auto const& r : lst) {
for (auto const &c : r) {
data.emplace_back(c);
}
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
Using std::vector::insert() (better and shorter)
As #Bob mentioned in a comment, you can use std::vector::insert() member function, instead of the inner emplace_back loop:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row{lst.size()}
, col{lst.size() ? lst.begin()->size() : 0} // Minimal validation
{
// Eliminate reallocations as we already know the size of the matrix
data.reserve(row * col);
for (auto const& r : lst) {
data.insert(data.end(), r.begin(), r.end());
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
So, we're saying: For each row (r) in the lst, insert the content of the row from the beginning (r.begin()) to the end (r.end()) into the end of the empty vector, data, (in an empty vector semantically we have: empty_vec.begin() == empty_vec.end()).
i might be a bit late but here is code for generally initializing tensors, regardless if they are matricies or vectors or whatever tensor.You could restrict it by throwing runtime errors when its not a matrix. Below is the source code to extract the data from the initilizer_list its a bit hacky. The whole trick is that the constructor are implicitly called with the correct type.
#include <initializer_list>
#include <iostream>
using namespace std;
class ShapeElem{
public:
ShapeElem* next;
int len;
ShapeElem(int _len,ShapeElem* _next): next(_next),len(_len){}
void print_shape(){
if (next != nullptr){
cout <<" "<< len;
next->print_shape();
}else{
cout << " " << len << "\n";
}
}
int array_len(){
if (next != nullptr){
return len*next->array_len();
}else{
return len;
}
}
};
template<class value_type>
class ArrayInit{
public:
void* data = nullptr;
size_t len;
bool is_final;
ArrayInit(std::initializer_list<value_type> init) : data((void*)init.begin()), len(init.size()),is_final(true){}
ArrayInit(std::initializer_list<ArrayInit<value_type>> init): data((void*)init.begin()), len(init.size()),is_final(false){}
ShapeElem* shape(){
if(is_final){
ShapeElem* out = new ShapeElem(len,nullptr);
}else{
ArrayInit<value_type>* first = (ArrayInit<value_type>*)data;
ShapeElem* out = new ShapeElem(len,first->shape());
}
}
void assign(value_type** pointer){
if(is_final){
for(size_t k = 0; k < len;k ++ ){
(*pointer)[k] = ( ((value_type*)data)[k]);
}
(*pointer) = (*pointer) + len;
}else{
ArrayInit<value_type>* data_array = (ArrayInit<value_type>*)data;
for(int k = 0;k < len;k++){
data_array[k].assign(pointer);
}
}
}
};
int main(){
auto x = ArrayInit<int>({{1,2,3},{92,1,3}});
auto shape = x.shape();
shape->print_shape();
int* data = new int[shape->array_len()];
int* running_pointer = data;
x.assign(&running_pointer);
for(int i = 0;i < shape->array_len();i++){
cout << " " << data[i];
}
cout << "\n";
}
outputs
2 3
1 2 3 92 1 3
The shape() function will return you the shape of the tensor at each dimension. The array is exactly saved as it is written down. It's really import to create something like shape since this will give you the ordering in which the elements are.
If you want a specific index out of the tensor lets say a[1][2][3]
the correct position is in 1*a.shape[1]a.shape[2] + 2a.shape[2] + 3
Some minor details and tricks can be found in: https://github.com/martinpflaum/multidimensional_array_cpp