What Is the Correct Way of Using C++11'S Range-Based For

What is the correct way of using C++11's range-based for?

TL;DR: Consider the following guidelines:

  1. For observing the elements, use the following syntax:

    for (const auto& elem : container)    // capture by const reference
    • If the objects are cheap to copy (like ints, doubles, etc.),
      it's possible to use a slightly simplified form:

        for (auto elem : container)    // capture by value
  2. For modifying the elements in place, use:

    for (auto& elem : container)    // capture by (non-const) reference
    • If the container uses "proxy iterators" (like std::vector<bool>), use:

        for (auto&& elem : container)    // capture by &&

Of course, if there is a need to make a local copy of the element inside the loop body, capturing by value (for (auto elem : container)) is a good choice.



Detailed Discussion

Let's start differentiating between observing the elements in the container
vs. modifying them in place.

Observing the elements

Let's consider a simple example:

vector<int> v = {1, 3, 5, 7, 9};

for (auto x : v)
cout << x << ' ';

The above code prints the elements (ints) in the vector:

1 3 5 7 9

Now consider another case, in which the vector elements are not just simple integers,
but instances of a more complex class, with custom copy constructor, etc.

// A sample test class, with custom copy semantics.
class X
{
public:
X()
: m_data(0)
{}

X(int data)
: m_data(data)
{}

~X()
{}

X(const X& other)
: m_data(other.m_data)
{ cout << "X copy ctor.\n"; }

X& operator=(const X& other)
{
m_data = other.m_data;
cout << "X copy assign.\n";
return *this;
}

int Get() const
{
return m_data;
}

private:
int m_data;
};

ostream& operator<<(ostream& os, const X& x)
{
os << x.Get();
return os;
}

If we use the above for (auto x : v) {...} syntax with this new class:

vector<X> v = {1, 3, 5, 7, 9};

cout << "\nElements:\n";
for (auto x : v)
{
cout << x << ' ';
}

the output is something like:

[... copy constructor calls for vector<X> initialization ...]

Elements:
X copy ctor.
1 X copy ctor.
3 X copy ctor.
5 X copy ctor.
7 X copy ctor.
9

As it can be read from the output, copy constructor calls are made during range-based for loop iterations.

This is because we are capturing the elements from the container by value
(the auto x part in for (auto x : v)).

This is inefficient code, e.g., if these elements are instances of std::string,
heap memory allocations can be done, with expensive trips to the memory manager, etc.
This is useless if we just want to observe the elements in a container.

So, a better syntax is available: capture by const reference, i.e. const auto&:

vector<X> v = {1, 3, 5, 7, 9};

cout << "\nElements:\n";
for (const auto& x : v)
{
cout << x << ' ';
}

Now the output is:

 [... copy constructor calls for vector<X> initialization ...]

Elements:
1 3 5 7 9

Without any spurious (and potentially expensive) copy constructor call.

So, when observing elements in a container (i.e., for read-only access),
the following syntax is fine for simple cheap-to-copy types, like int, double, etc.:

for (auto elem : container) 

Else, capturing by const reference is better in the general case,
to avoid useless (and potentially expensive) copy constructor calls:

for (const auto& elem : container) 

Modifying the elements in the container

If we want to modify the elements in a container using range-based for,
the above for (auto elem : container) and for (const auto& elem : container)
syntaxes are wrong.

In fact, in the former case, elem stores a copy of the original
element, so modifications done to it are just lost and not stored persistently
in the container, e.g.:

vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v) // <-- capture by value (copy)
x *= 10; // <-- a local temporary copy ("x") is modified,
// *not* the original vector element.

for (auto x : v)
cout << x << ' ';

The output is just the initial sequence:

1 3 5 7 9

Instead, an attempt of using for (const auto& x : v) just fails to compile.

g++ outputs an error message something like this:

TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x'
x *= 10;
^

The correct approach in this case is capturing by non-const reference:

vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
x *= 10;

for (auto x : v)
cout << x << ' ';

The output is (as expected):

10 30 50 70 90

This for (auto& elem : container) syntax works also for more complex types,
e.g. considering a vector<string>:

vector<string> v = {"Bob", "Jeff", "Connie"};

// Modify elements in place: use "auto &"
for (auto& x : v)
x = "Hi " + x + "!";

// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
cout << x << ' ';

the output is:

Hi Bob! Hi Jeff! Hi Connie!

The special case of proxy iterators

Suppose we have a vector<bool>, and we want to invert the logical boolean state
of its elements, using the above syntax:

vector<bool> v = {true, false, false, true};
for (auto& x : v)
x = !x;

The above code fails to compile.

g++ outputs an error message similar to this:

TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of
type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen
ce {aka std::_Bit_reference}'
for (auto& x : v)
^

The problem is that std::vector template is specialized for bool, with an
implementation that packs the bools to optimize space (each boolean value is
stored in one bit, eight "boolean" bits in a byte).

Because of that (since it's not possible to return a reference to a single bit),
vector<bool> uses a so-called "proxy iterator" pattern.
A "proxy iterator" is an iterator that, when dereferenced, does not yield an
ordinary bool &, but instead returns (by value) a temporary object,
which is a proxy class convertible to bool.
(See also this question and related answers here on StackOverflow.)

To modify in place the elements of vector<bool>, a new kind of syntax (using auto&&)
must be used:

for (auto&& x : v)
x = !x;

The following code works fine:

vector<bool> v = {true, false, false, true};

// Invert boolean status
for (auto&& x : v) // <-- note use of "auto&&" for proxy iterators
x = !x;

// Print new element values
cout << boolalpha;
for (const auto& x : v)
cout << x << ' ';

and outputs:

false true true false

Note that the for (auto&& elem : container) syntax also works in the other cases
of ordinary (non-proxy) iterators (e.g. for a vector<int> or a vector<string>).

(As a side note, the aforementioned "observing" syntax of for (const auto& elem : container) works fine also for the proxy iterator case.)

Summary

The above discussion can be summarized in the following guidelines:

  1. For observing the elements, use the following syntax:

    for (const auto& elem : container)    // capture by const reference
    • If the objects are cheap to copy (like ints, doubles, etc.),
      it's possible to use a slightly simplified form:

        for (auto elem : container)    // capture by value
  2. For modifying the elements in place, use:

    for (auto& elem : container)    // capture by (non-const) reference
    • If the container uses "proxy iterators" (like std::vector<bool>), use:

        for (auto&& elem : container)    // capture by &&

Of course, if there is a need to make a local copy of the element inside the loop body, capturing by value (for (auto elem : container)) is a good choice.



Additional notes on generic code

In generic code, since we can't make assumptions about generic type T being cheap to copy, in observing mode it's safe to always use for (const auto& elem : container).

(This won't trigger potentially expensive useless copies, will work just fine also for cheap-to-copy types like int, and also for containers using proxy-iterators, like std::vector<bool>.)

Moreover, in modifying mode, if we want generic code to work also in case of proxy-iterators, the best option is for (auto&& elem : container).

(This will work just fine also for containers using ordinary non-proxy-iterators, like std::vector<int> or std::vector<string>.)

So, in generic code, the following guidelines can be provided:

  1. For observing the elements, use:

    for (const auto& elem : container)
  2. For modifying the elements in place, use:

    for (auto&& elem : container)

range-based for in c++11

No, unluckily. See what the standard says:

The range-based for statement
for ( for-range-declaration : expression ) statement
is equivalent to

{
auto && __range = ( expression );
for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}

where __range, __begin, and __end are variables defined for exposition only

In other words, it already iterates from begin to end and already dereferences the iterator, which you never get to see.

C++11 syntax 'type var : var' is called 'range-based for'

for (char16_t c : str16)

The syntax above defines a range-based for loop (introduced in C++11). It essentially says:

Loop over each character in str16, copy the character into a variable called c and allow me to use it.

For example:

for (char16_t c : str16)
{
std::cout << c << std::endl;
}

Your second example is a K&R-style C function definition, and is obsolete. See here for more information: Function declaration: K&R vs ANSI

Can range-based C++11 for do/check extra operations/conditions?

Unfortunately, you can't put the increment into the range based for loop. However, in your specific case - as std::vector stores its elements contigously in memory - you can simulate option 2 by falling back to pointers (thanks to @M.M and @Jarod42 for corrections and improvements):

for ( const int& val : v )  {
std::cout << "v at index " << &val-v.data() << " is " << val;
}

more generic:

for ( const auto& val : v )  {
std::cout << "v at index " << std::addressof(val)-v.data() << " is " << val;
}

The other thing you can do is to write a index_range class, that represents a collections of indexes over which you can iterate in your range based for loop:

struct index_range_it {
size_t idx;
size_t operator*(){
return idx;
}
index_range_it& operator++() {
idx++;
return (*this);
}
};

bool operator!=(index_range_it l,index_range_it r) {
return l.idx != r.idx;
}

struct index_range {
size_t size;
index_range_it end(){return index_range_it{size};}
index_range_it begin(){return index_range_it{0};}
};

int main()
{
for (auto i: index_range{v.size()}){
std::cout << "v at index " << i << " is " << v[i];
}
}

A full fledged implementation of this idea can be found e.g. here

Such a range can then also be composed to something, where the iterator returns a proxy object containing the index as well as a reference to the current object and with c++17's structured binding that would be even more convenient to use.

Is it safe to use a C++11 range-based for-loop with an rvalue range-init?

Yes, it's perfectly safe.

From [class.temporary]/4-5:

There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when a default constructor is called [...]

The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference
except:

  • A temporary bound to a reference member in a constructor’s ctor-initializer [...]
  • A temporary bound to a reference parameter in a function call [...]
  • The lifetime of a temporary bound to the returned value in a function return statement [...]
  • A temporary bound to a reference in a new-initializer [...]

None of those exceptions apply. The temporary thus persists for the lifetime of the reference, __range, which is the entire loop.

C++11 Using Range-based for loop (for each) for dynamic array

Just use a container-like wrapper:

template <typename T>
struct Wrapper
{
T* ptr;
std::size_t length;
};

template <typename T>
Wrapper<T> make_wrapper(T* ptr, std::size_t len) {return {ptr, len};}

template <typename T>
T* begin(Wrapper<T> w) {return w.ptr;}

template <typename T>
T* end(Wrapper<T> w) {return begin(w) + w.length;}

Usage:

for (auto i : make_wrapper(a, sizeof a / sizeof *a))
std::cout << i << ", ";**

Demo.

With C++1Z we will hopefully be able to use std::array_view instead.

C++11 range-based for loops without loop variable

There may be a way to do it but I very much doubt it would be more elegant. What you have in that first loop is already the correct way to do it, limiting the scope/lifetime of the loop variable.

I would simply ignore the unused variable warning (it's only an indication from the compiler that something may be wrong, after all) or use the compiler facilities (if available) to simply turn off the warning at that point.

This may be possible with some sort of #pragma depending on your environment, or some implementations allow you to do things like:

for (int x = 0; x < 10; ++x) {
(void)x;

// Other code goes here, that does not reference "x".
}

I've seen that void trick used for unused parameters in function bodies.

How does the C++11 range-based for loop know the array size?

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).

Is there a range class in C++11 for use with range based for loops?

The C++ standard library does not have one, but Boost.Range has boost::counting_range, which certainly qualifies. You could also use boost::irange, which is a bit more focused in scope.

C++20's range library will allow you to do this via view::iota(start, end).

Can C++11 and C++17 Range-Based For Loop iterate to a specific position instead of full range of the map?

You either need an external counter to make early exit, eg:

int n = 0;
for(auto [k, v] : map)
{
if(++n > 10) break;
std::cout << k << ": " << v << std::endl;
}

Or, if you are not afraid of copying the map, you can do:

std::map<...> copy { map.begin(), std::next(map.begin(), 10) };

for(auto [k, v] : copy) std::cout << k << ": " << v << std::endl;

Finally, if you can use C++20, then you can simply do this:

#include <ranges>
for(auto [k, v] : map | std::views::take(10))
{
std::cout << k << ": " << v << std::endl;
}


Related Topics



Leave a reply



Submit