Ranges overkill?

The latest article from Rainer Grimm ( http://www.modernescpp.com/index.php/ranges-improvements-with-c-23 ) shows examples of using std::ranges to produce a container (std::vector here) containing ints starting at specified value, ending at specified value and being incrementing by specified value.

Nice code and all that. But IMO this is vastly over-complicated for such a potential simple task. I know the article is re ranges and this is an example, but I'm starting to see ranges being used where IMO they are an overkill for the task. It's almost like the answer is std::ranges - so what was the question.

I would code this as :

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
44
45
46
47
48
49
#include <vector>
#include <iostream>
#include <type_traits>

template<typename T>
typename std::enable_if_t< std::is_arithmetic_v<T>, std::vector<T>>
range(T begin, T end, T stepsize = 1) {
	std::vector<T> result {};

	if (begin < end)
		for (; begin < end; result.push_back(begin), begin += stepsize);
	else
		for (; begin > end; result.push_back(begin), begin += stepsize);

	return result;
}

int main() {
	std::cout << '\n';

	// range(1, 50)                                       // (1)
	auto res { range(1, 50) };

	for (const auto i : res)
		std::cout << i << " ";

	std::cout << "\n\n";

	// range(1, 50, 5)                                    // (2)
	res = range(1, 50, 5);
	for (const auto i : res)
		std::cout << i << " ";

	std::cout << "\n\n";

	// range(50, 10, -1)                                  // (3)
	res = range(50, 10, -1);
	for (const auto i : res)
		std::cout << i << " ";

	std::cout << "\n\n";

	// range(50, 10, -5)                                  // (4)
	res = range(50, 10, -5);
	for (const auto i : res)
		std::cout << i << " ";

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


Which does the same job with the same output but IMO is simpler - both in code terms and understanding.

Any thoughts on this?
I haven't started using std::ranges yet, the pipe syntax still looks a bit scary to me, so I'm not a good judge to say if it's overkill or over-complicated, but one obvious problem with your code is that you construct a std::vector every time which is far from optimal.
Last edited on
but one obvious problem with your code is that you construct a std::vector every time which is far from optimal.


?? a vector is only created once in the function with elements pushed-back as required. The ranges code also creates the vector in the function and uses push_back.

Yes, the number of elements required could be calculated in advance and the vector capacity reserved before the loop.
Ah, you're right. I assumed things without looking. In that case I personally don't see much of an improvement with using ranges. Regular loops are very powerful and flexible, easy to modify, etc. I like them, although I must say I wasn't expecting to see push_back in the increment expression of the for loop.
With vector reserve:

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
44
45
46
47
48
49
50
51
52
#include <vector>
#include <iostream>
#include <type_traits>
#include <cmath>

template<typename T>
typename std::enable_if_t< std::is_arithmetic_v<T>, std::vector<T>>
range(T begin, T end, T stepsize = 1) {
	std::vector<T> result {};

	result.reserve(static_cast<size_t>(std::ceil((0.0 + end - begin ) / stepsize)));

	if (begin < end)
		for (; begin < end; result.push_back(begin), begin += stepsize);
	else
		for (; begin > end; result.push_back(begin), begin += stepsize);

	return result;
}

int main() {
	std::cout << '\n';

	// range(1, 50)                                       // (1)
	auto res { range(1, 50) };

	for (const auto i : res)
		std::cout << i << " ";

	std::cout << "\n\n";

	// range(1, 50, 5)                                    // (2)
	res = range(1, 50, 5);
	for (const auto i : res)
		std::cout << i << " ";

	std::cout << "\n\n";

	// range(50, 10, -1)                                  // (3)
	res = range(50, 10, -1);
	for (const auto i : res)
		std::cout << i << " ";

	std::cout << "\n\n";

	// range(50, 10, -5)                                  // (4)
	res = range(50, 10, -5);
	for (const auto i : res)
		std::cout << i << " ";

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

Here's a talk about std::ranges that shows better use cases.
https://www.youtube.com/watch?v=d9ToM7sIvq0&t=478s

An alternativ for std::ranges is linqcpp.
https://github.com/DevUtilsNet/linqcpp
Registered users can post here. Sign in or register to post.