3

From cplusplus.com :

Internally, an array does not keep any data other than the elements it contains (not even its size, which is a template parameter, fixed on compile time).

I understand that this means that using array is similar to using int[] and sizeof in the same scope. But is this code valid or is relying on undefined behavior?

class A {
    array<int, 10> arr;
    void setArr() {
        for (int& i : arr)
            i = 42;
    }
    void printArr() {
        for (int i : arr)
            cout << i << endl;
    }
};

How does the compiler know when to stop the foreach without storing the array size on the heap or stack? I ran it, the code works.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
ytoledano
  • 3,003
  • 2
  • 24
  • 39

3 Answers3

12

It's saying something more and the response is in your quotation:

[...] not even its size, which is a template parameter, fixed on compile time [...]

As an example, the following code is legal as well:

template<int N>
struct C {
    int size() { return N; }
};

As you can see, here we do the same and N is not kept anyway, but it is known for it's a template parameter, fixed on compile time.

The same is valid for the templated class std::array, that accepts a template parameter that defines its size. Because of that, the size is known (and fixed) at compile time and it is implicitly part of the generated type, even though no extra space is reserved at runtime.

EDIT (accordingly with the comments)

Of course, you cannot change at runtime the size of such an array by simply invoking one of its methods. As stated here:

std::array is a container that encapsulates fixed size arrays.

Also, it wouldn't make sense to dynamically change its size, for it would be no longer coherent with the template parameter that defines the actual size. Because of that, the response is obviously: no, you cannot change its size (even though you can use that array to fill another array having a different size, of course).

However, you have a bunch of benefits for that:

The struct combines the performance and accessibility of a C-style array with the benefits of a standard container, such as knowing its own size, supporting assignment, random access iterators, etc.

It's up to you to decide if it's worth to use it instead of a plain, C-style array. It mainly depends on the problem you are facing with, so I cannot say that.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • 2
    So actually, the length is part of the type. This is quite different than a C style array. Using this array, I can't replace it value with an array of a different size because it would also by of a different type. – ytoledano Nov 29 '15 at 11:52
  • 1
    @ytoledano C style array length is also part of the C style array's type – Cubbi Nov 29 '15 at 16:39
7

When we say that std::array doesn't store its size we mean that it does not reserve memory to store its size during runtime.

Because you said std::array<int, 10>, the compiler knows that the size of the array is 10. So it can correctly implement std::array::end. Using that, the range-based for loop knows when to stop.

orlp
  • 112,504
  • 36
  • 218
  • 315
5

Standard class std::array has member functions begin and end that return iterators that point to the first element of the array and to the position after the last actual element.

iterator begin() noexcept;
const_iterator begin() const noexcept;
iterator end() noexcept;
const_iterator end() const noexcept;

When a range-based for statement is executed the compiler searches names begin and end for the class and uses them to set the valid range.

For example this loop

for (int i : arr)
    cout << i << endl

in fact internally is substituted by the compiler for the following loop

for ( auto first = arr.begin(), last = arr.end(); first != last; ++first )
{
    int i = *first;
    cout << i << endl;
}

As the class is declared like

template <class T, size_t N >
struct array;

then inside the class definition value N, the number of elements in the array, is acceptable by other members of the class including member funcions end()

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335