TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #852 4.4K
std::less vs std::ranges::less
#опытным

В догонку к предыдущим постам. Вот у вас есть структурка TIme из С++17:

struct Time {
int hours;
int minutes;
int seconds;

bool operator<(const Time &other) const {
return std::tie(hours, minutes, seconds) <
std::tie(other.hours, other.minutes, other.seconds);
}
};


Вы как-то заполняете ее и сортируете:

std::vector<Time> times = {
{14, 30, 15}, {9, 45, 0}, {14, 30, 0}, {23, 59, 59}, {0, 0, 0}};

std::sort(times.begin(), times.end());


И все работает прекрасно.

И тут решили вы перейти на С++20 и заменить все алгоритмы классической stl на алгоритмы ренджей. Синтаксис у них понятнее, да и почему бы и нет. Изменения тут будут тривиальные:

std::vector<Time> times = {
{14, 30, 15}, {9, 45, 0}, {14, 30, 0}, {23, 59, 59}, {0, 0, 0}};

std::ranges::sort(times);


Запускаете сборку, а она падает с какими-то нечитаемыми ошибки, типа таких:

/usr/include/c++/15/concepts:362:13:   required for the satisfaction of 'invocable<_Fn, _Args ...>' [with _Fn = std::ranges::less&; _Args = {Time&, Time&}]
/usr/include/c++/15/concepts:366:13: required for the satisfaction of 'regular_invocable<_Fn, _Args ...>' [with _Fn = std::ranges::less&; _Args = {Time&, Time&}]
/usr/include/c++/15/concepts:370:13: required for the satisfaction of 'predicate<_Rel, _Tp, _Tp>' [with _Rel = std::ranges::less&; _Tp = Time&]
/usr/include/c++/15/concepts:375:13: required for the satisfaction of 'relation<_Rel, _Tp, _Up>' [with _Rel = std::ranges::less&; _Tp = Time&; _Up = Time&]
/usr/include/c++/15/concepts:385:13: required for the satisfaction of 'strict_weak_order<_Fn&, typename std::__detail::__indirect_value<_Iter>::type, typename std::__detail::__indirect_value<_I2>::type>' [with _Fn = std::ranges::less; _I1 = __gnu_cxx::__normal_iterator<Time*, std::vector<Time, std::allocator<Time> > >; _I2 = __gnu_cxx::__normal_iterator<Time*, std::vector<Time, std::allocator<Time> > >]


Нам говорили, что с концептами ошибки станут понятнее, но это только в теории. А на практике вот это.

В чем в итоге проблема?


По умолчанию в сортировке рэнджей используется компаратор std::ranges::less, а в обычной сортировке - std::less. И в их разнице и зарыта собака: std::ranges::less требует определения всех шести операторов сравнения или одного spaceship'а и все заработает:

struct Time {
int hours;
int minutes;
int seconds;

// Spaceship operator (генерирует все 6 операторов сравнения)
auto operator<=>(const Time& other) const = default;
};

std::vector<Time> times = {
{14, 30, 15}, {9, 45, 0}, {14, 30, 0}, {23, 59, 59}, {0, 0, 0}};

std::ranges::sort(times);


Ну или можете пометить оператор< дефотным и передавать в сортировку std::less:
struct Time {
int hours;
int minutes;
int seconds;

bool operator<(const Time& other) const = default; // Here
};

std::vector<Time> times = {
{14, 30, 15}, {9, 45, 0}, {14, 30, 0}, {23, 59, 59}, {0, 0, 0}};

std::ranges::sort(times, std::less{}); // And here


Пару раз натыкался на эту проблему, поэтому решил поделиться болью.

Give clear feedback. Stay cool.
  • ❤ 48
  • 👍 25
  • 🔥 19
  • 🤯 1
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →