9

When I do something like this:

int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array) {
    x *= 2;
}

C++11 obviously knows that my array only has 5 elements. Is this information stored somewhere in the my_array object?

If so, is there any good reason why it is not made available to me as a developer (or is it?!?!?)? It seems that a lot of the world's problems would be solved if C++ devs always knew the bounds of the arrays they're dealing with.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
MrFox
  • 1,244
  • 1
  • 11
  • 24

3 Answers3

11

This is simply something that the language requires to work, and the compiler must implement. Obviously the complete type of my_array is int[5] (i.e. the size is part of the type), so this information is readily available.

Contrary to popular belief, there is no use of the free std::begin()/std::end() functions in play, although those would naively seem to be able to do the trick (but there's a catch involving ADL that would break this approach).

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Ok. So is there any particular reason why a my_array.size does not exist? – MrFox Nov 13 '12 at 20:30
  • 6
    @suslik: Sure, because arrays are not class types and thus cannot have member functions. But you can trivially write an `array_size` free function template that produces the desired value, or use the readily-made [`std::extent`](http://en.cppreference.com/w/cpp/types/extent). – Kerrek SB Nov 13 '12 at 20:30
  • How did I live my life not knowing about this for so long... I blame std::vector :). – MrFox Nov 13 '12 at 20:53
  • 3
    @suslik: Because `std::extent` is new C++11 stuff too. Those mechanics (the type of arrays being part of the type) aren't new, so `std::extent` could always have been written. They just didn't until recently. – Nicol Bolas Nov 14 '12 at 00:01
6

It is available - you can define begin and end on arrays in standard C++. The size of the array is encoded in the type.

The general method is to use references to arrays.

Here's an example size function:

template<typename T, size_t N>
size_t array_size(T (& const)[N])
{
    return N;
}
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • 6
    This is also known as `std::extent`. And `std::begin` and `std::end` are *already* defined for arrays in the standard library. – Kerrek SB Nov 13 '12 at 20:32
5

No, it's not part of the object. But it's part of the type. That's what the 5 in the array declaration is. However, this won't work:

void f(int arr[5]) {
    for(int& x: arr) {
        // whatever
    }
}

because the name of the array here decays into a pointer to its first element, that is, the argument declaration is equivalent to int *arr which has no size information.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • 3
    I wouldn't use the term 'decay' in this instance. I think that term is usually specific to the implicit conversion done to an array variable in many expressions. `int a[5]; a + 1; // decay` Instead I like to refer to what happens here with the term 'adjust' as that's the word the standard uses. `void foo(int a[5]); // type 'adjustment': equivalent to void foo(int *a)`. And I like to use a sarcastic tone when I say the word. – bames53 Nov 13 '12 at 20:50
  • For reference, @balki's solution doesn't work with unsized array references, e.g. `(&arr)[5]`. But it can usually be made to work for generic-sized arrays by using the array size as a template parameter: see https://stackoverflow.com/questions/26182907/range-based-for-loop-on-array-passed-to-non-main-function – andybuckley Sep 08 '16 at 21:01