range-based for loops in c++
Asked Answered
V

3

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.

Versed answered 21/1, 2013 at 13:35 Comment(3)
You can always know the size of an array (gist.github.com/3959946#file-arrays-h-L33). If you convert that to pointer, you stop knowing the size.Rookery
@R.MartinhoFernandes: Nice gist!Lebanon
In general you will live a much happier c++ life by foregoing plain arrays and moving to standard libraries containersLisandralisbeth
L
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;
}
Laliberte answered 21/1, 2013 at 13:41 Comment(4)
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)Versed
@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.Laliberte
in other words it works on an array on the stack, not on the heap, right?Versed
@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.Laliberte
D
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.

Dearden answered 21/1, 2013 at 13:46 Comment(11)
That would be his own dumb fault anyway for using new[].Calf
@DeadMG: "Dumb" is not the same as "Unknowing".Johnette
@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.Dearden
in otherwords, this is an array vs vector argument, not an argument against dynamic memory allocation of a C arrayVersed
@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.Dearden
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.Versed
@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.Dearden
I'm sure vector has overhead beyond use of the operator[]Versed
@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.Dearden
@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?Antoninaantonino
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 wayVersed
I
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
}
Isaacs answered 21/1, 2013 at 13:37 Comment(7)
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 ?Versed
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.Isaacs
@SebbyJohanns the type of the fixed size array contains the size. then you use template parameter deduction to get it.Laliberte
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.Versed
@SebbyJohanns, the type (compile-time), not the value (run-time)Erickaericksen
@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.Isaacs
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.Antoninaantonino

© 2022 - 2024 — McMap. All rights reserved.