Why is std::range::sort
(and other range-based algorithms) implemented in the range
namespace? Why isn't it defined as an overload of std::sort
taking a range?

- 2,079
- 1
- 17
- 28
-
1My guess would be that it is to avoid making the control over overload resolution into a nightmare for the library specification and implementors. – StoryTeller - Unslander Monica Oct 24 '20 at 21:11
-
@StoryTeller-UnslanderMonica Couldn't they use concepts to take care of that? – Touloudou Oct 24 '20 at 21:11
-
1They'd need to go over every *existing* overload and update the conditions under which it is removed from an overload set. Concepts or not it's still ALOT of work to make sure it behaves correctly. I doubt there'd be ranges support in C++20 at all, had they gone that route. – StoryTeller - Unslander Monica Oct 24 '20 at 21:13
-
Could it be a potentially breaking change? Allowing `std::sort(range, compare)` could change the meaning of `std::sort(begin, end)` in existing code. – HolyBlackCat Oct 24 '20 at 21:17
-
@HolyBlackCat I don't think so. The 2nd arguments would satisfy different concepts. – cigien Oct 24 '20 at 21:18
-
1@cigien What if something is a valid comparator and a valid iterator at the same time? :P That's unlikely, but could be the case. – HolyBlackCat Oct 24 '20 at 21:22
-
@HolyBlackCat Hmm, you may be right. It would be a strange type indeed, but it might not break any rules :) – cigien Oct 24 '20 at 21:23
1 Answers
It's to avoid disrupting existing code bases. Eric Niebler, Sean Parent and Andrew Sutton discussed different approaches in their design paper D4128.
3.3.6 Algorithm Return Types are Changed to Accommodate Sentinels
... In similar fashion, most algorithm get new return types when they are generalized to support sentinels. This is a source-breaking change in many cases. In some cases, like
for_each
, the change is unlikely to be very disruptive. In other cases it may be more so. Merely accepting the breakage is clearly not acceptable. We can imagine three ways to mitigate the problem:
Only change the return type when the types of the iterator and the sentinel differ. This leads to a slightly more complicated interface that may confuse users. It also greatly complicates generic code, which would need metaprogramming logic just to use the result of calling some algorithms. For this reason, this possibility is not explored here.
Make the new return type of the algorithms implicitly convertible to the old return type. Consider
copy
, which currently returns the ending position of the output iterator. When changed to accommodate sentinels, the return type would be changed to something likepair<I, O>;
that is, a pair of the input and output iterators. Instead of returning apair
, we could return a kind of pair that is implicitly convertible to its second argument. This avoids breakage in some, but not all, scenarios. This subterfuge is unlikely to go completely unnoticed.Deliver the new standard library in a separate namespace that users must opt into. In that case, no code is broken until the user explicitly ports their code. The user would have to accommodate the changed return types then. An automated upgrade tool similar to clang modernize can greatly help here.
We, the authors, prefer (3).
Ultimately, it was to be the least disruptive to existing code bases that move onto building using C++20 enabled compilers. It's the approach they themselves preferred, and seems like the rest is history.

- 165,132
- 21
- 377
- 458
-
1I didn't think about the return type that would need to accommodate both the old and new code. Makes sense, thanks. – Touloudou Oct 24 '20 at 21:26
-
2What, no love for `rfor_each` `rtransform`? Why use a namespace when you can prefix everything with some stupid extra characters? (I kid) – Yakk - Adam Nevraumont Oct 26 '20 at 14:15
-
1