61

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    @r3musn0x I think this is probably a broader question than the one you’ve linked to. – Tim Oct 03 '19 at 19:02
  • 1
    @Tim, I agree, however the accepted answer to that question is way more comprehensive and it covers both of them. – r3mus n0x Oct 03 '19 at 19:11
  • 1
    As an aside, it's against the C++ standard to name your locals with double underscores. https://stackoverflow.com/a/228797/ – Max Barraclough Oct 05 '19 at 14:59

2 Answers2

82

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.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • It would be more logical to have this update together with Ranges, probably. Iterator-sentinel ranges are still very restricted in C++17. – L. F. Oct 03 '19 at 05:33
  • 2
    @L.F. IIRC ranges was originally slated to be in C++17, instead we got a TS since it wasn't quite ready. We still need this if we want to use the ranges TS, or ranges-v3. – NathanOliver Oct 03 '19 at 12:30
  • 2
    @L.F. Ranges are a library feature. There is range-v3 lib on github that served as the basis for standardization and it benefited from this standardiese change. – Dan M. Oct 03 '19 at 13:12
  • 1
    Including the feature in C++17 proper also allows programmers to implement their own sentinels (or similar) if desired, and can potentially allow bugs to be discovered before the associated library additions are ready, @L.F. – Justin Time - Reinstate Monica Oct 03 '19 at 18:30
32

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;
danadam
  • 3,350
  • 20
  • 18