27

Working with legacy code, I asked myself should I replace fixed sized C-style arrays with the new std::array? E. g.

static const int TABLE_SIZE = 64;
double table[TABLE_SIZE];

replace with

std::array<double, TABLE_SIZE> table;

While I see the benefits from using std::vector for variable size arrays, I don't see them with fixed size. table.size() is known anyways, std::begin(), std::end() as free functions can be used for STL-algorithms with C-style arrays too. So besides being more standard compliant, do I miss more benefits? Is it worth the work to replace all occurrences or is it considered to be best practice?

DaBrain
  • 3,065
  • 4
  • 21
  • 28
  • 11
    Another benefit of std::array is that it does not decay into a pointer when passed to a function which seems to be a common stumbling block for new programmers using static arrays. – eerorika Mar 21 '14 at 23:25
  • I agree, that can be useful for beginners – Marco A. Mar 21 '14 at 23:26
  • 1
    I like the fact that it doesn't decay into a pointer, however in order for this to work you have to explicitly state the number of items you want the parameter type to have. If you need a function accepting an array with arbitrary size, you will need a template function, which should be implemented iterator-based. And from there on you can pass `begin` / `end` from a C-style array as well, avoiding the pointer-decay. – Excelcius Mar 22 '14 at 10:21
  • 2
    @Excelcius "If you need a function accepting an array with arbitrary size", use `std::initializer_list` since this is precisely why it exists and how it's implemented. – underscore_d Jun 26 '16 at 10:04
  • @underscore_d You're right, that's even better. – Excelcius Jun 27 '16 at 12:26

7 Answers7

15

AFAIK std::array just provides a nicer STL-like interface to deal with rather than normal C-arrays. In terms of performances and capabilities the two choices boil down to be pretty much the same but std::array can be used as a standard container.

Another feature: they might be treated like tuples since they provide tuple-like access functions.

Last but not the least as user2079303 noticed: if your code is going to be used by novice programmers, it can prevent the array decaying process when passing it as an argument.

If you're wondering if you should just replace all your C-style arrays with std::arrays, the answer is yes if you're going to exploit some of the newly available features and/or rewrite some of your code to take advantage of those capabilities. If you're just plain substituting C-arrays with std::arrays (without touching anything else) it might not be worth it.

Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • How does a `std::array` support move semantics in a way that a normal array does not? You cannot move an `std::array`, `move_iterator` works with both. Construction of elements works the same. – pmr Mar 21 '14 at 23:28
  • it's just a feature, not an advantage. By the way I'll remove it since it can be intended as "better over C arrays because of this" – Marco A. Mar 21 '14 at 23:29
7

There is one more thing that I don't see mentioned yet: std::array::at(). Stack corruptions can be really nasty to debug and bugs due to stack corruption can hide for a long while. :( The situtation has become somewhat better with Address Sanitizer. Still, I prefer bound checking over address sanitizer.

In my opinion bound-checked element access is enough reason in itself to use std::array rather than C style arrays.

Ali
  • 56,466
  • 29
  • 168
  • 265
  • 2
    If you need bound-checked element access then your code already has a bug. – ildjarn Mar 23 '14 at 09:05
  • @ildjarn Yes, bound checking is useful for discovering bugs. How do you know for sure that your code is bug-free if you don't test it? Don't get me wrong, if performance matters or I need vector instructions, I use address sanitizer to check for out of bound indices. – Ali Mar 23 '14 at 11:28
  • 3
    Assertions are for validating preconditions, not exceptions. – ildjarn Mar 23 '14 at 13:27
  • 5
    Yeah, `.at()` _kinda_ makes sense for `map` et al, but not at all for `std::array`. Avoid like the plague. Compilers provide debug versions of `std::array` that do bounds-checking through compile options, rather than requiring you to find-and-replace everything to the superior `[]` once you're indexing sanely and realise how many cycles `.at()` is wasting. – underscore_d Jun 26 '16 at 10:08
5

An additional feature that std::array has is that it can be copy-assigned;

std::array<int, 3> a = {1, 2, 3};
std::array<int, 3> b = {4, 5, 6};

std::array<int, 3> c = a;         // copy-construction
a = b;                            // copy-assignment
Zyx 2000
  • 1,625
  • 1
  • 12
  • 18
4

IMHO, you should never change working, legacy code without a really good reason. If you are developing an enhancement, or fixing a bug and the files in question were going to be modified anyway then I'd say sure go ahead and make the change. Otherwise, you probably have better things to do with your time. There is always risk associated with changing source code that can lead to an extensive amount of time debugging when you could have been doing more productive things.

shawn1874
  • 1,288
  • 1
  • 10
  • 26
4

It depends on the use case. Performance and space-wise, they should be identical, except for the size member that std::array carries. EDIT: Even the size will be optimized out. So it´s identical even space-wise. Implementations usually can do:

template <class T, std::size_t N>
class array {
...
public:
    contsexpr std::size_t size() const { return N; }

}

And get away storing the size of the string.

std::array<T, N>won't decay to a pointer when passing it around. But in your use case you have a global variable, I guess. I wouldn´t change what it's already working just for making it look nicer.

On the other hand, if you want to pass an std::arrayof any size to a function, you will have to template it:

template <std::size_t N>
void f(std::array<MyT, N> const & arr);

Pros of std::array

  • Safer, they don't decay to pointers.
  • They carry the size.

Cons of std::array

  • If you want a general function taking std::array, you will have to do it in the header as a template.

In C arrays, you can do this:

void f(MyT const * arr, std::size_t size);

and it would work with any array, no matter the length. This is less safe BUT more convenient to hide dependencies in .cpp files, since later you can code the function inside a .cpp file.

But as I said, taking into account your use case, choose. I wouldn't change code that is working and is not gonna be exposed (for example, if it is a global that is never taken as a parameter in functions) in any unsafe way.

Germán Diago
  • 7,473
  • 1
  • 36
  • 59
  • If it is about methods (that I can hide by making them private) then I use two overloads: `private` for the unsafe version, and `std::array` version as `public` that is just an `inline` wrapper. This way I can ensure that parameters are correct (safety) but I'm also able to hide implementation in cpp file. And if your parameter array needs to be of specific length, std::array is probably the only way to guarantee the right size of input. Then you can even hide it in cpp file without any wrappers. – j_kubik Jan 08 '16 at 00:50
2

I think std::array has many advantages over c-style arrays. So it is worthwhile to work to replace all occurrences or at least start using the std::array instead of c-style. For more information we can look here mentioned by C++11 FAQ by Bjarne Stroustrup.

  • It has no space overheads beyond what it needs to hold its elements.
  • It does not use free store,and it can be initialized with an initializer list
  • It knows its size (number of elements).
  • It doesn't convert to a pointer unless you explicitly ask it to.
  • standard array's features makes it attractive for embedded systems programming (and similar constrained, performance-critical, or safety critical tasks) .

In other words, it is very much like a built-in array without the problems.

Regarding your point to table.size() is known, but it is valid only if somebody use the array in the context where it has been defined. If we need to pass it to some other function, then it is not known and we had to pass it.

Mantosh Kumar
  • 5,659
  • 3
  • 24
  • 48
  • But if you need to pass a std::array to other functions, unlike std::vector, you'll need a template solution. As Excelcius comment already points out, this ends up with either passing the size or using iterators which works with c-style arrays. No benefit for me. – DaBrain Mar 22 '14 at 12:49
  • Well of course you need the template solution in order to make full use of std::array.However it should be noted that you would not have any(almost) any performance plenty over c-style arrays.However size information encapsulated with the object is really marvellous to have. In one of the talk Bjarne had said that array is very good but stupid data structure as it do not know its own size. – Mantosh Kumar Mar 22 '14 at 13:11
  • In addition to all its advantages std::array has at least one drawback compared to C arrays - its size can't be deduced from initializer. `int c_arr[] = {1, 3, 4, 6, 7, 0}; // OK` but `std::array cpp_arr = {1, 3, 4, 6, 7, 0}; // not OK` – user2665887 Mar 22 '14 at 20:54
  • @user2665887: Well it is by design to avoid any extra space used to store the size. Hence such template metaprogramming concept was used to design it(.i.e. to pass size as one template argument and its not optional as well so if you do it would be compile time error). You should remember that c-arrays has directly supported using the language and std::array is normal c++ code.There would be some minor advantage if something is supported by language itself. However you should try to view it in broader and future perspective. – Mantosh Kumar Mar 23 '14 at 02:27
1

There is a bunch of things:

  • member functions so it looks like every other sequence in the standard library
  • tuple like access (get, tuple_size, tuple_element) with compile time checked bounds access
  • does not decay to a pointer

The first point is pretty obvious and mainly a benefit in generic code. The second point is a little obscure: from a semantic arrayS are nothing else but degenerate tupleS (of course tupleS do not guarantee sequential storage in memory). Treating them as such makes sense and code working on tuples can work largely with arrays as well. The third point is a nice bonus and adds a little safety to code.

pmr
  • 58,701
  • 10
  • 113
  • 156