5

It would seem that the "for each" style of syntax available in C++11 permits array iteration without knowledge of the actual size of the array (number of elements). I assume, since it is part of the new standard, that this is perfectly safe, even for C arrays. Typically, you must also separately know the size of a C array before manipulating it, but I want verification from anyone experienced with this new C++ technique that it works exactly as you'd expect:

extern float bunch[100];

for (float &f : bunch) {
  f += someNumber;
}

Is there anything I should know about non-obvious side effects or disadvantages to this technique? It doesn't show much in code I see, probably because most of the code was written before this was in the standard. Want to make sure its rare usage isn't because of some other reason not well-known.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
johnbakers
  • 24,158
  • 24
  • 130
  • 258

3 Answers3

4

There is nothing strange or unsafe about that usage. The array's size is known at compile time, so it can be used in the loop. This is an example of a template function that allows you to find out the length of an array:

template< class T, std::size_t N >
std::size_t length( const T (&)[N] )
{
  return N;
}

Foo f[5];
std::cout << length(f) << "\n";

This should make it clear that you cannot use this technique, or range based loops, on dynamically sized C-style arrays.

Of course, if you have range based loops then you should also have std::array (if not, you can probably get ti from std::tr1 or boost), so you can avoid the C-style array entirely:

extern std::array<float, 100> bunch;

for (auto &f : bunch) {
  f += someNumber;
}
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • Ah, perhaps my confusion then is the difference between a C array and `std::array` -- the range based is valid for the latter, but not for the former, right? Because I always thought and had read that a C array does not provide knowledge about its own size (length) – johnbakers Jan 21 '13 at 14:10
  • 1
    @SebbyJohanns no, it is valid for both, as long as the C-style array is statically sized. The template function above shows you that you can get the size back from such an array. – juanchopanza Jan 21 '13 at 14:15
  • in other words it works on an array on the stack, not on the heap, right? – johnbakers Jan 21 '13 at 14:17
  • 1
    @SebbyJohanns Yes, kind of. It would also work on an array in static storage, but that is just a detail. Basically, when you allocate an array on "the heap", the problem is that all you have as a handle is a pointer. The size information is completely lost. Which is a compelling reason not to use heap allocated arrays directly. – juanchopanza Jan 21 '13 at 14:19
4

It is perfectly safe to use a range-based for-loop with arrays. I suppose you're worried that you might accidentally use it on a pointer:

float* array = new float[100];
for (float& f : array) {
    // ...
}

Don't worry though. The compiler will produce an error in this case. So in cases where it's not safe, you'll get a compilation error anyway.

Nikos C.
  • 50,738
  • 9
  • 71
  • 96
  • That would be his own dumb fault anyway for using `new[]`. – Puppy Jan 21 '13 at 13:51
  • 1
    @DeadMG: "Dumb" is not the same as "Unknowing". – Sebastian Mach Jan 21 '13 at 14:08
  • @SebbyJohanns He refers to the C++11 guarantee of `std::vector` using continuous memory. So when using C++11, there's no reason to ever use `new[]` anyway; you can always use `std::vector` instead. – Nikos C. Jan 21 '13 at 14:13
  • in otherwords, this is an array vs vector argument, not an argument against dynamic memory allocation of a C array – johnbakers Jan 21 '13 at 14:14
  • @SebbyJohanns That's how I understood his comment. And it's a valid point; C arrays are for C. This is C++. And with C++11, you can even use an `std::vector` together with code that expects plain dynamic arrays. So there's no reason anymore to not use `std::vector`. – Nikos C. Jan 21 '13 at 14:17
  • There is a reason if you are using arrays to store thousands of OpenGL vertices getting sent to the GPU 60 times per second. The overhead of a vector is not as desirable as an array. – johnbakers Jan 21 '13 at 14:20
  • @SebbyJohanns In release builds, `operator[]` should have zero overhead and be equivalent to referencing the underlying data directly. In debug builds it usually doesn't though. – Nikos C. Jan 21 '13 at 14:33
  • I'm sure `vector` has overhead beyond use of the `operator[]` – johnbakers Jan 21 '13 at 14:35
  • @SebbyJohanns Only profiling can give you that information. Personally I never encountered any overhead with vectors that would have any real effect. But of course I never wrote a GL app that has to deal with thousands of vertices. – Nikos C. Jan 21 '13 at 14:41
  • @SebbyJohanns, what overhead of `vector` makes it a problem to store thousands of vertices in a `vector`? Do you have numbers and measurements or just a feeling in your bones? – Jonathan Wakely Jan 21 '13 at 16:06
  • I'm new to C++ and working through numerous graphics tutorials, all of which comment on the increased overhead of using complex object models for the constant manipulation of vertices. While 'vector' is used for other purposes in these programs, the authors always favor basic structs and arrays for the actual storing of vertex positions and colors. I'm just following that, I don't have enough knowledge either way – johnbakers Jan 22 '13 at 00:14
2

Arrays can be passed as references and have their type and length deduced.

#include <iostream>

template<typename T, size_t N>
void fun(T const (&arr)[N])
{
    for (std::size_t i = 0; i < N; ++i)
       std::cout << arr[i] << " ";
}

int main()
{
   int x[] = { 1, 2, 3 }; // no need to set length here
   fun(x); // 1 2 3, no need to specify type and length here
}
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 1
    It's unclear to me in looking at this template how the `size_t` for `N` is passed into the function `fun` -- how is it extracting the value for `N` ? – johnbakers Jan 21 '13 at 14:12
  • During template argument deduction, the compiler looks at the argument (x which is of type int[3]) and the formal parameter (arr which is of type T[N]) and substituting int for T and 3 for N gives an exact match. – TemplateRex Jan 21 '13 at 14:16
  • @SebbyJohanns the type of the fixed size array contains the size. then you use template parameter deduction to get it. – juanchopanza Jan 21 '13 at 14:16
  • Ah ok, so an array not created with `new` actually stores its own size. this is news to me and I assume is a c++ feature, not a C feature. – johnbakers Jan 21 '13 at 14:19
  • @SebbyJohanns, the type (compile-time), not the value (run-time) – rici Jan 21 '13 at 14:26
  • 1
    @SebbyJohanns no the size is not stored inside the array variable, it's just that with template functions the compiler can look up the definition of the array type and deduce the size. – TemplateRex Jan 21 '13 at 14:27
  • To put it another way, an object of type `int[3]` does not need to "store" its size, the size is a property of the type, it's 3. That's true whether it's created on the stack or on the heap, but when you do `new int[3]` you only get returned `int*` which does not tell you the real type, so you cannot tell the size. It still _has_ a size, you just can't find it out. – Jonathan Wakely Jan 21 '13 at 16:11