Question on odd use of brace initializers for arrays in C++

Advertisements

I am currently going through and linting a lot of old code at a company that has been around a while.

We have recently been having issues with uninitialized C++ members having garbage data, which is one reason for the housekeeping.

Unfortunately with all the updates to C++ over the years there are a few caveats that are beyond my understanding.

I know that if you declare and initialize an array in the header such as:

float m_thisArray[16]{}; // direct initialization
or:

float m_thatArray[16] = {}; // copy initialization

that all the values should be set to 0.0f automatically.

However this week I have also seen:
float m_floatArray[16];
in the header followed by:
*m_floatArray = {};
in the constructor.

Is that second one guaranteed to zero all the elements? It would not have been my first choice, but all I really know at this point is that it does not throw a compile error.

>Solution :

Is that second one guaranteed to zero all the elements?

No, it sets the first and only the first element of the array to zero.

m_floatArray is an array lvalue and the built-in * expects a rvalue as operand. So array-to-pointer conversion is applied to m_floatArray, resulting in a pointer to the first element of m_floatArray. *m_floatArray then dereferences that pointer, resulting in a float lvalue referring to the first element of the array. This element is then assigned to in *m_floatArray = {}. The assignment for a float lvalue means setting to zero, but it applies only to that single element that *m_floatArray refers to.

I am not really sure how the original author got the idea to write that line. With just m_floatArray = {} the compiler would complain that arrays aren’t assignable, but I don’t see how one would get the idea to add * from that.

To set all the elements in the array to zero in the constructor use the constructor’s member initializer list, where you can write m_floatArray{} or m_floatArray() to initialize all elements to zero. Alternatively to the constructor you can also use one the default member initializers you mentioned in the class definition itself.

If this isn’t possible in initialization, then use std::fill.

This is also another unusual example of why std::array ought to be preferred over built-in arrays. This unintuitive array-to-pointer decay wouldn’t happen with std::array.

Leave a ReplyCancel reply