C++ Logo

std-proposals

Advanced search

[std-proposals] for-loops revision (simplification and new syntax)

From: Pau Miquel Montequi Hernandez <pau.montequi_at_[hidden]>
Date: Mon, 10 Feb 2025 15:17:56 +0100
Greetings,

I would like to discuss a revision of the for loop syntax, with the goal of
evaluating possible improvements that could enhance its usability.

*Common usage of for loops*
The typical use case of a for loop is iterating over a collection of items
when the number of iterations is known beforehand. More often than not,
this means iterating one-by-one, yet the syntax requires explicitly
specifying the increment operation:

for (int i = 0; i != 42; ++i) // Most of the time, ++i is the expected
> behavior


This raises the question: Can we simplify this pattern while keeping it
expressive?

*Proposal: defaulting increment and condition*
One approach would be to default the increment operation to pre-increment
and assume a typical end condition:

for (int i = 0; 42) // Implicitly increments i and checks against 42


This unsettling syntax leads to another question: can we make it more
intuitive?

*Alternative syntax inspired by range-for loops*
A syntax resembling C++11's range-for loop can be considered:

for (int i = 0 : 42) // Iterates from 0 to 42
> for (int id = get_first_id() : get_last_id())
> for (auto i = v.rbegin() : v.rend()) // Reverse iteration


However, using colon (:) could be problematic since the C++11 range-for
loop already follows a specific expansion involving std::begin()/std::end() and
unary operator* which may lead to confusion.

*Possible concerns and alternative syntax*
The C++11 syntax assumes iterators and unary operator*, while this proposal
does not. An alternative syntax can reduce ambiguity:

   - for (int i = 0 ... 42).
   - for (int i = 0 -> 42).
   - for (int i = 0 => 42).
   - for (int i = 0 <- 42).
   - for (int i = 0 <= 42).

Each has different implications, like <-/<= explicitly suggesting reverse
iteration.

Also traditional for loops allow variables from the enclosing scope to be
accessed on and after the loop, whereas range-based for loops do not, for
consistency would the proposed new syntax force the same behaviour?

int x = 0;
> for (x : 42) // Error?
> if (f(x))
> break;
>
> return x; // Error?



*Additional considerations*
Some related improvements could be discussed in parallel:

Allowing a for loop to not require a named variable if it is not going to
be used:

> for (0 : tries) { if (try_to_connect()) return true; }


More flexible for loop initialization, allowing multiple init statements:

> template <auto size>
> void lcase(const char (&in)[size], char (&out)[size])
> {
> for (auto b = in, e = in + size; auto o = out; b != e; ++b, ++o)
> *o = std::tolower(*b);
> }


*Feedback*
I’d like to gather feedback on:

   - Is simplifying for loops for common use cases desirable?
   - Then, an alternative syntax is necessary or adapting C++11’s range-for
   loop could be a better approach?
   - If an alternative syntax is the best approach, will <-/<= and ->/=>
   provide a good way to express reverse iteration?
   - Will it be useful to extend range-for loop reach in order to allow the
   use of existing variables from the outer scope?
   - Any feedback about the additional considerations.


Looking forward to your thoughts.

-- 
Pablo Miguel Montequi Hernández.

Received on 2025-02-10 14:18:09