decrement size_t in loop

Pages: 12
seeplus wrote:
You would code a decrementing size_t loop as below. Alternatively use a reverse iterator

With C++20 there is a third way, a reverse range-based for loop using <ranges>.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <string>
#include <ranges>

int main()
{
   std::string str { "C++ is Cool!" };

   std::cout << str << '\n';

   // old school for loop
   for (size_t i { }; i < str.size(); ++i) { std::cout << str[i] << ' '; }
   std::cout << '\n';

   // for loop using const iterators
   for (auto itr { str.cbegin() }; itr != str.cend(); ++itr) { std::cout << *itr << ' '; }
   std::cout << '\n';

   // https://en.cppreference.com/w/cpp/language/range-for
   for (const char& itr : str) { std::cout << itr << ' '; }
   std::cout << "\n\n";

   // old school reverse for loop
   for (size_t i { str.size() }; i > 0; --i) { std::cout << str[i - 1] << ' '; }
   std::cout << '\n';

   // reverse for loop with const iterators
   for (auto itr { str.crbegin() }; itr != str.crend(); ++itr) { std::cout << *itr << ' '; }
   std::cout << '\n';

   // https://www.fluentcpp.com/2020/02/11/reverse-for-loops-in-cpp/
   for (const char& itr : str | std::views::reverse) { std::cout << itr << ' '; }
   std::cout << "\n\n";

   // range-based for loops work with regular arrays as well
   int arr[] { 5, 10, 15, 20, 25 };

   for (const int& itr : arr) { std::cout << itr << ' '; }
   std::cout << '\n';

   for (const int& itr : arr | std::views::reverse) { std::cout << itr << ' '; }
   std::cout << '\n';
}
C++ is Cool!
C + +   i s   C o o l !
C + +   i s   C o o l !
C + +   i s   C o o l !

! l o o C   s i   + + C
! l o o C   s i   + + C
! l o o C   s i   + + C

5 10 15 20 25
25 20 15 10 5
a reverse range-based for loop using <ranges>.


From the OP first post, the decrement (or increment depending upon coding) needs to be specified. Range-for does an assumed inc/dec of 1

So for iterator (with consecutive storage):

1
2
3
4
5
6
7
8
9
10
11
12
#include <vector>
#include <iostream>

int main() {
	constexpr size_t mydec { 2 };
	const std::vector nums { 1, 2, 3, 4, 5 };

	for (auto i { nums.rbegin() }; i < nums.rend(); i += mydec)
		std::cout << *i << ' ';

	std::cout << '\n';
}

Last edited on
Even with consecutive storage, where the iterators model std::contiguous_iterator, this still is not guaranteed to work.
https://en.cppreference.com/w/cpp/iterator/contiguous_iterator

For example the above program may crash as exhibited here:
https://coliru.stacked-crooked.com/a/403fb2018f44583e

The value of i that's supposed to cause the loop to terminate, is not generally "reachable" from the prior value of i. This violates the semantic requirements of std::contiguous_iterator.

If the iterators were merely pointers you're still hosed because of [expr.rel]/4.
https://eel.is/c++draft/expr.rel#4
Interesting. That approach works fine with VS release...

Obviously de-referencing an out-of-range iterator is wrong.

Last edited on
Isn't the -D_GLIBCXX_DEBUG flag a non-standard compiler extension for GCC?

Compile without it and the code works as expected. Same expectation as with VS which doesn't have AFAIK a comparable make flag.
> That approach works fine with VS release...

>> Compile without it and the code works as expected.

The sane option is to avoid undefined behaviour in our code, even if it may appear to "work fine" or "as expected" on a particular implementation (on the occasions when we tried it).

Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
https://eel.is/c++draft/intro.defs#defns.undefined
Topic archived. No new replies allowed.
Pages: 12