initialize array to 0 in constructor [duplicate] - c++

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;

Related

How to pass array initialization thru custom structure?

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.

C++: array<> too many initializers

The following code is returning the compilation error below. I'm stuck understanding how there are too many initializers. This code works using vector<X>. Does anyone know why the error is being reported and how to resolve? Thanks
#include <iostream>
#include <array>
using namespace std;
struct X {
int x, y;
};
int main(int argc, char *argv[])
{
array<X,2> a0 = {{0,1}, {2,3}};
for (auto& p : a0) {
cout << p.x << endl;
cout << p.y << endl;
}
return 0;
}
Compilation:
g++ -pedantic -Wall test116.cc && ./a.out
test116.cc: In function ‘int main(int, char**)’:
test116.cc:11:34: error: too many initializers for ‘std::array<X, 2>’
array<X,2> a0 = {{0,1}, {2,3}};
Try
array<X,2> a0 = {{{0,1}, {2,3}}};
Note the extra set of braces.
It seems a bit odd but it's this way because the only member of array is the actual array:
template <class T, size_t N>
class array {
T val[N];
// ...
};
The constructors are all implicitly defined so that array ends up being a trivially constructable type.
You may use one of the following initializations because std::array is an aggregate that contains another aggregate as its data member.
array<X,2> a0 = { { { 0, 1 }, { 2, 3 } } };
array<X,2> a0 = { 0, 1, 2, 3 };
array<X,2> a0 = { { 0, 1, 2, 3 } };
array<X,2> a0 = { { 0, 1, { 2, 3 } } };
array<X,2> a0 = { { { 0, 1 }, 2, 3 } };
I found that my problem is related to the version of g++,9.4.0 is ok and 5.4.0 not.version of g++.By the way, the version of g++ is associated with the system version generally.

Creating an array of a class with arguments in constructor

I'm trying to instantiate an array of a class where the constructor takes two arguments, and initialize it in the same line.
Conceptually, I want to do something like this:
foo::foo (int A, int B = 10)
{
V1 = A;
V2 = B;
}
foo Myfoo[3] = { (1, 100), (2, 300), (5, 100) };
// what I _don't_ want to do is create individual objects like this:
// foo Myfoo1(1, 100);
// foo Myfoo2(2, 300);
// foo Myfoo3(5, 100);
What I found is that when the constructor is called the arguments are not as expected. The B argument always shows up as the default value of 10. Just in tinkering I threw in additional arguments in the array initialization.
foo Myfoo[3] = { (0, 1, 100), (2, 300, 0), (0, 5, 100, 0) };
To my surprise it compiled without error, but I didn't pursue this too far, because it didn't make sense to me - but I was able to affect the problem
Does anyone have an idea on how I should code this? I've already worked around the problem but I'm curious how it should be done properly.
With the usage of (1, 100), you're just passing one int with value 100 to the constructor of foo. The comma-operator just discards the 1st operand, and returns the 2nd operand here. (It doesn't work in the way as you expected like foo Myfoo1(1, 100); or foo(1, 100);.)
You should use {} ( list initialization (since C++11) ) instead of (), i.e.
foo Myfoo[3] = { {1, 100}, {2, 300}, {5, 100} };
The cause of the problem has already been explaind by #songhuanyao.
I can think of the following ways to resolve the problem.
Option 1
Use {} instead of () to construct objects.
foo Myfoo[3] = { {1, 100}, {2, 300}, {5, 100}};
Option 2
Use foo explitly with () to construct objects.
foo Myfoo[3] = { foo(1, 100), foo(2, 300), foo(5, 100) };
Option 3
Use foo explitly with {} to construct objects.
foo Myfoo[3] = { foo{1, 100}, foo{2, 300}, foo{5, 100}};
We need to use {} instead of () and code is working as expected
#include<iostream>
using namespace std;
class Test
{
public:
Test(int a, int b = 10)
{
x = a;
y = b;
cout << "C-tor" << endl;
}
void show()
{
cout << " x = " << x << " y = " << y << endl;
}
private:
int x;
int y;
};
int main()
{
Test obj[3] = {{10,20},{30,40}, {50,60}};
obj[0].show();
obj[1].show();
obj[2].show();
return 0;
}
Above code produces expected output:
C-tor
C-tor
C-tor
x = 10 y = 20
x = 30 y = 40
x = 50 y = 60
I hope it helps!

C++11 for loop in a Template Function

I am trying to write a function which would Print Data on the console. The function is to be templated as it should accept different types of Data.
The code is as shown below:
template<typename DataType>
void PrintData(DataType *X)
{
for (DataType Data : X)
{
cout << Data << "\t";
}
cout << endl;
}
int main()
{
int nArray[7] = { 7, 5, 4, 3, 9, 8, 6 };
double dArray[5] = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
system("pause");
return EXIT_SUCCESS;
}
I get an error that variable Data is undeclared in the templated function PrintData.
error C2065: 'Data' : undeclared identifier
error C3312: no callable 'begin' function found for type 'double *'
error C3312: no callable 'begin' function found for type 'int *'
error C3312: no callable 'end' function found for type 'double *'
error C3312: no callable 'end' function found for type 'int *'
Any help would be appreciated.
Thanks
Assuming you have included the iostream header file and the using namespace std; directive. Then your problems are:
You should not use DataType *. Your code makes X a pointer, which is different from array. Use DataType const& or DataType&& instead.
You have to include the iterator header file which provides the begin and end function for C-style array.
The following code works for me.
#include <iostream>
#include <iterator>
using namespace std;
template<typename DataType>
void PrintData(DataType const& X)
{
for (auto Data : X)
{
cout << Data << "\t";
}
cout << endl;
}
int main()
{
int nArray[7] = { 7, 5, 4, 3, 9, 8, 6 };
double dArray[5] = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
return EXIT_SUCCESS;
}
As commented by Igor Tandetnik, you may use template<struct DataType, size_t N> if you want to refer the size of the array.
Update:
With template<struct DataType> and DataType *X, DataType is deduced as int and double, and X is a pointer which is not a container.
With template<struct DataType, size_t N> and DataType (&X)[N], DataType is deduced as int and double, and X is an array which can be used with range-based for loop.
With template<struct DataType> and DataType&& X or DataType const& X, DataType is deduced as int[7] or double[5], and X is an array as well.
The problem is that you're passing the name of the array (e.g., nArray), which is just a pointer to the first element, basically. However, new-style for loops expect something on which you can call begin and end - a range.
The following changes make it work by using a vector instead of an array. Note that there are very few differences otherwise, and, in general, very few reason to use C-style arrays in contemporary C++.
#include <iostream>
#include <vector>
using namespace std;
template<typename DataType>
void PrintData(const DataType &X)
{
for (const auto &Data : X)
{
cout << Data << "\t";
}
cout << endl;
}
int main()
{
vector<int> nArray = { 7, 5, 4, 3, 9, 8, 6 };
vector<double> dArray = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
system("pause");
return EXIT_SUCCESS;
You have some problems:
1) DataType must be a container. So, try to use:
std::vector<int> nArray = { 7, 5, 4, 3, 9, 8, 6 };
std::vector<double> dArray = { 4.3, 2.5, -0.9, 100.2, 3.0 };
2) You are not going to change the container. It's better to pass container by const reference:
template<typename DataType>
void PrintData(const DataType & X);
3) Change the loop like this:
for (const auto & value : X) {
std::cout << value << "\t";
}
Code example:
#include <iostream>
#include <vector>
template<typename DataType>
void PrintData(const DataType & X) {
for (const auto & value : X) {
std::cout << value << "\t";
}
std::cout << std::endl;
}
int main() {
std::vector<int> nArray = { 7, 5, 4, 3, 9, 8, 6 };
std::vector<double> dArray = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
return 0;
}
As Igor suggested, If at all you wish to use DataType * then you need to do something like this
#include <iostream>
#include <iterator>
using namespace std;
template <typename DataType, size_t N>
void PrintData(DataType (&X)[N])
{
for (auto i : X)
cout << i << "\t";
cout << endl;
}
int main()
{
int nArray[7] = { 7, 5, 4, 3, 9, 8, 6 };
double dArray[5] = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
return EXIT_SUCCESS;
}
Output
7 5 4 3 9 8 6
4.3 2.5 -0.9 100.2 3
Explanation:
If you see void PrintData(int* nArray); & void PrintData(int (&nArray)[7] );
are similar declarations, except that seconds one tells where array ends.
Template function
template <typename DataType, size_t N>
void PrintData(DataType (&X)[N])
is deduced as'
void PrintData(int (&nArray)[7] )
You could also write
void PrintData(int (&nArray)[7] )
{
for (auto i : nArray)
cout << i << "\t";
cout << endl;
}

Passing array of size 9 and becomes size 1

simple call by reference
void foo(int* A)
{
// ...
}
void main()
{
int A[] = {1,1,1,1,1,1,1,1,1};
foo(A);
}
not sure why but it is lessening the size of the array and losing/leaking information on the array....
You're passing a pointer to the first element of an array. Create your function with the prototype
void foo(int* A, int size);
You will still be able to access A[0...size-1] like normal.
You should totally drop C-style arrays and use std::array instead. Just compare this (which is the solution to your problem):
void foo(int* A, std::size_t size) {
// ...
}
int main() {
int A[] = {1,1,1,1,1,1,1,1,1};
foo(A, (sizeof(A) / sizeof(int)));
}
to:
template<std::size_t Size>
void foo(const std::array<int, Size>& array) {
// ...
}
int main() {
std::array<int, 9> A {{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }};
foo(A);
}
Ain't it beautiful? Or just take a look at how gorgeous it is with std::vector:
void foo(const std::vector<int>& vector) {
// vector.size() is the size
}
int main() {
std::vector<int> A = { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
foo(A);
}
And if you really want foo to be a generic algorithm, I'll just blow your mind with iterators:
template<class Iterator>
void foo(Iterator begin, Iterator end) {
// ...
}
int main() {
std::array<int, 9> A {{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }};
std::vector<int> B = { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
foo(A.begin(), A.end()); // not a single problem
foo(B.begin(), B.end()); // was given that day
}
C++ has an amazing (arguably) standard library and an amazing type system (if you don't overlook C legacy "features", like void*): use them.
not sure why but it is lessening the size of the array and losing/leaking information on the array....
In foo(), sizeof(A) == 8 not because it is "leaking" information, but because 8 is the size of the pointer of type int*. This is true regardless of how many integers A was initialized with in main().
This may shine some light on what is happening:
#include<iostream>
using namespace std;
void foo(int* A)
{
cout << "foo: " << sizeof(A) << endl; // 8
}
void bar(int A[])
{
cout << "bar: " << sizeof(A) << endl; // still 8
}
int main()
{
int A[] = {1,1,1,1,1,1,1,1,1};
cout << "main: " << sizeof(A) << endl; // 36 (=4*9)
foo(A);
bar(A);
return 0;
}
Output:
main: 36
foo: 8
bar: 8
In main, sizeof "knows" the size of A[] - it is sizeof(int) * length = 4 * 9 = 36. This information is lost when A[] is cast to a pointer A* in foo.
What if we pass in A as bar(int A[]) instead. Will that retain the array length? No! In that case, sizeof(A) is still 8, the size of the pointer. Only in main does the compiler retain the information of array size of A.
If you want your functions to know the size of the array, use the std::vector<int> template, or pass in the size separately.
Here's another discussion on this: When a function has a specific-size array parameter, why is it replaced with a pointer?
The correct way to pass array by reference is
void foo(int (&A) [9])
{
// sizeof(A) == sizeof(int) * 9
}
the generic way is so:
template <std::size_t N>
void foo(int (&A) [N])
{
// sizeof(A) == sizeof(int) * N
}
You may use std::array (C++11 required) which has a syntax more intuitive
template <std::size_t N>
void foo(std::array<int, N> &A)
{
// A.size() == N
}