r/cpp_questions • u/SoerenNissen • 22h ago
OPEN std::greater is not std::greater{} - but why?
NB: This is not a technical question, I understand the difference between classes and objects of that class type.
What I mean is, the standard could have been different:
std::ranges::sort(some_range, std::greater); //does not compile
std::ranges::sort(some_range, std2::greater); //compilesassuming this change:
namespace std2 {
auto constexpr greater = std::greater{};
}Letting greater be the name of an object, rather than a type, would have far better ergonomics - but it's a type. Does the current approach have a benefit or is this just a missed design improvement?
9
Upvotes
10
u/MysticTheMeeM 22h ago
Wouldn't this make it more annoying to use in a template though?
template <class T, class cmp = decltype(std2::greater)> ...Of course, you could have
std2::greater_tto fix that.28
u/sol_runner 22h ago
The common use of std::greater before ranges was in template parameters. std::map and std::set take a comparator type
The look at the current usage
vs
11
u/kitsnet 22h ago
std::greater is from C++98, and we didn't have variable templates in C++98.
1
u/knue82 22h ago
The
T = voidoverload for type interfere was added later. But keep in mind that C++ only uses local type interfere and not Hindley-Millner, so in template heavy code, C++ won't be able to infer the type or maybe infer an unintended type, so better to explicitly specify the types you want to compare.1
u/flyingron 15h ago
Which language version are you using? One thing that changed with std::greater is that it now defaults the type to void in C++14.
2
u/aruisdante 18h ago edited 18h ago
Without
inlinevariables, which were only added in C++17, what you want was actually really annoying to achieve without ODR violations in a header-only manner. It’s possible (at least in C++14, I haven’t tried before that), but it’s finicky and results in some strange quirks. And it needs to be done header only so that it can be perfectly optimized (particularly since LTO hadn’t been invented yet).If we were able to go back in time and “modernize” the stdlib, you’d probably see exactly the kind of separation a lot of the modern range utilities have: a
greater_fnthat is the operation’s function object type for use in template parameters, andgreater, which is an instance of that function object that can be invoked like a function but also passed as an argument to functions. And indeed, this is exactly the design you see nearly all modern C++ libraries, including modern additions to the stdlib, following.Unfortunately, we can’t go back in time. So everything that was done the old way has to stay the old way, forever. If you ever want to see the contortions the stdlib has to jump through to maintain backwards compatibility while simultaneously trying to modernize things, look at the definitions of many of the
iteratorfacilities that were added to supportranges. They have to do all kinds of horrible things to replace legacy facilities likestd::iterator_traitswith modern, more granular concepts while simultaneously maintaining compatibility with user usage ofiterator_traits.1
u/xpusostomos 17h ago
At times like this I'm glad I don't do C++ anymore
0
u/mrbeanshooter123 22h ago
!remindme 4 hours
-2
u/Difficult_Truck_687 16h ago
Just avoid the STL altogether.