Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why did the range based 'for' loop specification change in C++17? [duplicate]

I was looking over some ugly code (that was modifying the underlying sequence while iterating), and to explore the definition of the range-based for loop, I went to cppreference.

There I noticed something strange:

The range based for loop changed in C++17, but I do not see the reason for the change, and the code looks the same to me (just "refactored"). So the old one was:

{
  auto && __range = range_expression;
  for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
  }
} 

The new one is

{
  auto && __range = range_expression;
  auto __begin = begin_expr;
  auto __end = end_expr;
  for ( ; __begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
  }
} 

Why was this change made, and does it make any legal C++14 programs exhibit undefined behavior (UB) in C++17?

like image 579
NoSenseEtAl Avatar asked Oct 02 '19 19:10

NoSenseEtAl


People also ask

How does range-based for loop work?

Range-based for loop in C++ Range-based for loop in C++ is added since C++ 11. It executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.

Can a for loop condition change?

No it doesn't. The first statement in the for loop is executed only once. Once i is set, changing the value of the variable that was used to set i does not effect i . You are allowed to change the value of i explicitly within the for loop body though.

How do range-based for loops work C++?

A range-based for loop terminates when one of these in statement is executed: a break , return , or goto to a labeled statement outside the range-based for loop. A continue statement in a range-based for loop terminates only the current iteration.

Are range-based for loops faster?

Range-for is as fast as possible since it caches the end iterator[citation provided], uses pre-increment and only dereferences the iterator once. Then, yes, range-for may be slightly faster, since it's also easier to write there's no reason not to use it (when appropriate).


Video Answer


2 Answers

Using

auto __begin = begin_expr, __end = end_expr;

requires both begin_expr and end_expr to return the same type. This means you cannot have a sentinel iterator type that is different from the beginning type. Using

auto __begin = begin_expr ;
auto __end = end_expr ;

fixes that issue while proving full backwards compatibility with C++14.

like image 77
NathanOliver Avatar answered Oct 20 '22 07:10

NathanOliver


It is explained later in the "notes":

As of C++17, the types of the begin_expr and the end_expr do not have to be the same ...

and you can't have that with:

auto __begin = begin_expr, __end = end_expr;
like image 25
danadam Avatar answered Oct 20 '22 07:10

danadam