10

I want to use a two dimensional array of constant size as a class member in C++. I have problems initializing it in the constructor though.

Here are my non-working tries:

1.)

class A {
 public:
  int a[2][2];
  A();
};

A::A() {
a = {{1,2},{2,4}};
}

yields: error: assigning to an array from an initializer list

2.)

class A {
 public:
  int a[2][2];
  A();
};

A::A() {
int b[2][2] = {{1,2},{2,4}};
a = b;
}

yields: invalid array assignment

3.)

class A {
 public:
  int **a;
  A();
};

A::A() {
int b[2][2] = {{1,2},{2,4}};
a = b;
}

yields: cannot convert ‘int [2][2]’ to ‘int**’ in assignment

I come from C background. I know that I could use std::vector and I am aware of the disadvantages this approach has but since this is an exercise for me I would like to know how to get it working with plain arrays. I should add that I want to work on this array later on. I want to change the stored values but not the size. Maybe that matters as well (I figured a const at the right place could help somehow?).

ravenfrost
  • 143
  • 1
  • 1
  • 8

5 Answers5

11

If you have C++11, you can use this syntax in the constructor definition:

A() : a{{1,2}, {3, 4}} {}

If you don't have C++11, you will need to stick to the wicked old ways:

A() {
  a[0][0] = 1;
  // etc
}

The first example also uses the constructor init-list, which should always be used to initialize members instead of intializing them in the constructor body.

pmr
  • 58,701
  • 10
  • 113
  • 156
  • 1
    As I understand it, the syntax used here is non-standard, a g++ language extension. – Cheers and hth. - Alf Apr 09 '14 at 22:50
  • @Cheersandhth.-Alf I tested this with both gcc and clang using `-std=c++11` (opposed to `std=gnu11`) and both accepted it. `clang` rejected `a({{1,2}, {3, 4}})` though as a GNU extension. – pmr Apr 09 '14 at 22:51
  • @Cheersandhth.-Alf Try to compile this code and your one with `-pedantic-errors` flag and... – Constructor Apr 09 '14 at 22:54
  • I've been using this for a while in GCC, thinking it was standard. Good to know. (Will try not to comment that I really think this should be standardized. Ops, did.) – Kahler Apr 09 '14 at 23:00
  • @Constructor: I think I'm wrong BUT my code compiles just fine with g++ 4.8.2 and option `-pedantic-errors`. However, Visual C++ 2013 reports "error C2536: 'A::A::m' : cannot specify explicit initializer for arrays". So in a practical sense the C++11 notation (if it is standard, which I now believe after checking and finding no language stating the opposite) is not yet portable. – Cheers and hth. - Alf Apr 09 '14 at 23:02
  • @Cheersandhth.-Alf Really? [g++ 4.8.1 diagnoses](http://rextester.com/ROECH20343) it as incorrect code with `-pedantic-errors`. – Constructor Apr 09 '14 at 23:05
  • In C++11 you could just have in the class definition `int a[2][2] = { {1,2}, {3,4} };` – M.M Apr 09 '14 at 23:11
  • @Cheersandhth.-Alf As I can see there is a [bug](http://connect.microsoft.com/VisualStudio/feedback/details/792161/constructor-initializer-list-does-not-support-braced-init-list-form) in VC++ (even in VC++2013). – Constructor Apr 09 '14 at 23:15
  • @MattMcNabb Yes, but I think in class definitions are something that should never been added to the standard in the first place and that you shouldn't ever use it. – pmr Apr 10 '14 at 11:05
  • They have their place, they can simplify code that was convoluted otherwise. They do break interface/implementation separation though - although the concept of private and protected member variables also break that. – M.M Apr 10 '14 at 11:16
  • @MattMcNabb They also break the rule that I need to look at the ctors to understand the invariants of a class after construction. Now I need to look everywhere. – pmr Apr 10 '14 at 11:19
  • You have to look at the variables anyway, a ctor might neglect to initialize them (so they have uninitialized values sometimes). – M.M Apr 10 '14 at 11:20
  • Since I just g++ I will go with that. Thanks a bunch. – ravenfrost Apr 10 '14 at 20:12
2

various multidimensional array in constructor by example:

// int array1[1];
A() : array1{0} {}

// int array2[2][2];
A() : array2{{0}} {}

// int array3[3][3][3];
A() : array3{{{0}}} {}
sailfish009
  • 2,561
  • 1
  • 24
  • 31
1

Try this, it works for bidimensional array (in standard C++):

 class A {
 public:
  int a[2][2];
  A();
};


typedef struct{ int a[4]; } array_t;

A::A() {

 int at[2][2] = {{1,2},{2,4}};

*(array_t*)a = *(array_t*)at;

}

Ciao Angelo

AngeloDM
  • 397
  • 1
  • 8
  • I think the assignment is undefined behaviour, although you could save the situation by using a `memcpy` or equivalent. – M.M Apr 10 '14 at 11:18
  • This technique forces the compiler to use the assignement operator for above array (supported for standard structures). Naturally, this is only valid when the size of the operands are equal. I use this example to show the power of the casting of the C language. However, I accept your comment and I will make a new question about this argument, so we can have other opinions about aove code. Thanks. – AngeloDM Apr 10 '14 at 11:59
  • The UB comes in before you get that far. It is undefined to alias a struct as an `int[2][2]`. (The Standard lists which types may be aliased with which types and this is not in the list). Further, it's possible that `at` may not be correctly aligned for `array_t *` (I'd have to check the wording of the alignment requirements to verify this) – M.M Apr 10 '14 at 12:08
  • I welcome your comments and I modified my solution, please tell me if you think that the new solution is more portable. Thanks. – AngeloDM Apr 10 '14 at 18:36
0

Your first variant is extremely close to the right C++11 syntax:

A::A()
    : a{{1,2},{2,4}}
{
}
Constructor
  • 7,273
  • 2
  • 24
  • 66
0

To complement the previous answers (you guys are so fast):

What you were trying to do in case 1 and 2 is array assignment, not permitted, as compiler says ;

But I would like to draw your attention to your third case, that's a grave misconception, specially coming from C as you say.

Assigning to a a pointer to a local variable?

Kahler
  • 1,130
  • 6
  • 24
  • Just for me and the sake of complete understanding: Doing so would render `**a` `NULL` after the constructor has ended right? I'm not exactly a pro in `C` either. – ravenfrost Apr 10 '14 at 20:08
  • No, no. The analogy is that `b` only exists inside where it was declared (the constructor) so if you take it's address, when the function ends, `a` will still have whatever value it was, but we can't guarantee what will lies there. The program may use that address for another purpose. Most likely, if you later try to access what `a` points to outside the function, an error about invalid memory will pop up, or some unreliable value will be read. – Kahler Apr 10 '14 at 21:30
  • Okay, I see now. Thank you for clearing that up for me. – ravenfrost Apr 11 '14 at 21:19