Array size constant antipattern

Translation of the article was prepared on the eve of the start of the course “C ++ Developer. Professional “

I want to draw your attention to the anti-pattern, which I often come across in the code of students on the Code Review StackExchange and even in a fairly large number of educational materials (!) Of other people. They have an array of, say, 5 elements; and then, since magic numbers are bad, they introduce a named constant to represent the cardinality of “5”.

``````void example()
{
constexpr int myArraySize = 5;
int myArray[myArraySize] = {2, 7, 1, 8, 2};
...``````

But the solution is so-so! In the above code, the number is five repeats: first in value `myArraySize = 5`and then again when you actually assign the elements `myArray`… The above code is just as terrible from a maintenance standpoint as:

``````constexpr int messageLength = 45;
const char message[messageLength] =
"Invalid input. Please enter a valid number.n";``````

– which, of course, none of us will ever write.

Repeated code is not good

Note that in both of the above code snippets, every time you change the contents of the array or the wording of the message, you must update two lines of code instead of one. Here’s an example of how a maintainer can update this code. wrong:

``````   constexpr int myArraySize = 5;
-   int myArray[myArraySize] = {2, 7, 1, 8, 2};
+   int myArray[myArraySize] = {3, 1, 4};``````

Patch above looks like as if it modifies the contents of the array with 2,7,1,8,2 on the 3,1,4but it is not! In fact, he changes it to 3,1,4,0,0 – padded with zeros – because the maintainer forgot to adjust `myArraySize` in accordance with `myArray`

Reliable approach

As for counting, computers are pretty damn good at it. So let the computer count!

``````int myArray[] = {2, 7, 1, 8, 2};
constexpr int myArraySize = std::size(myArray);``````

Now you can change the contents of the array, say with 2,7,1,8,2 on the 3,1,4by changing just one line of code. You don’t need to duplicate the change anywhere.

Even more, to manipulate `myArray` real code usually uses loops `for` and / or algorithms based on the range of the iterator, so it won’t need a named variable at all to store the size of the array.

``````for (int elt : myArray) {
use(elt);
}
std::sort(myArray.begin(), myArray.end());
std::ranges::sort(myArray);

// Warning: Unused variable 'myArraySize'``````

In the “bad” version of this code `myArraySize` always used (in the declaration `myArray`), and therefore the programmer is unlikely to see that it can be excluded. In the “good” version, it is easy for the compiler to find that `myArraySize` not used.

How to do it with `std::array`?

Sometimes a programmer takes another step towards the Dark Side and writes:

``````constexpr int myArraySize = 5;
std::array<int, myArraySize> myArray = {2, 7, 1, 8, 2};``````

This should be rewritten as at least:

``````std::array<int, 5> myArray = {2, 7, 1, 8, 2};
constexpr int myArraySize = myArray.size();  // или std::size(myArray)``````

However, there is no easy way to get rid of the manual counting on the first line. CTAD C ++ 17 lets you write

``std::array myArray = {2, 7, 1, 8, 2};``

but this only works if you need an array `int` – it won’t work if you need an array `short`for example or an array `uint32_t`

C ++ 20 gives us std :: to_arraywhich allows us to write

``````auto myArray = std::to_array<int>({2, 7, 1, 8, 2});
constexpr int myArraySize = myArray.size();``````

Note that this creates a C array and then move (move-constructs) its elements in `std::array`… All of our previous examples have initialized `myArray` using a curly-braced initializer list that triggered aggregate initialization and instantiated the array elements directly in place.

In any case, all of these options result in a large number of additional template instances compared to good old C arrays (which don’t require template instantiation). Therefore, I strongly prefer `T[]` newer `std::array<T, N>`

In C ++ 11 and C ++ 14, `std::array` had the ergonomic advantage of being able to say `arr.size()`; but that advantage evaporated when C ++ 17 provided us with `std::size(arr)` and for inline arrays. Have `std::array` there are no more ergonomic advantages. Use it if you need its whole object variable semantics (pass the entire array to a function! Return an array from a function! Assign arrays with =! Compare arrays with ==!), But otherwise I recommend avoiding using `std::array`

Likewise, I recommend avoiding `std::list`if you don’t need the stability of its iterator, fast gluing, sorting without replacing elements, etc. I’m not saying that there is no place for these types in C ++; I’m just saying that they have a “very specific set of skills,” and if you don’t use those skills, you are likely overpaying.

Conclusions: do not fence the cart in front of the horse. In fact, the cart may not even be needed. And if you need to use the zebra to do the horse’s work, you also shouldn’t fence the cart in front of the zebra.