Pull to refresh

Comments 30

Прочитал статью, но так и не понял, чему равно "5<=>2". ЧЯДНТ?

Возвращает ">", как я понял. Корректный знак неравенства.

Приводить ассемблерный вывод и не указывать ключи компиляции и компилятор - моветон. Ещё лучше было бы дать ссылку на godbolt.

Я не использовал такой оператор. Но, как я понял, одна его реализация позволяет заменить перегрузку всех операторов сравнения.

Зачем так сложно сделали? Аж 3 типа с результатами сравнения... Достаточно было одного, имеющего 4 значения: "больше", "меньше", "равно" и "несравнимо". Это красиво ложится на 2 бита, которые можно представить как специальное знаковое целое число, имеющее соответственно коды +1, -1, 0 и -2 (которое "несравнимо", но в нашем случае это было бы не отрицательное число, а специальный код - аналог NaN для целых чисел, который не сравнивался бы с целыми числами).

А то что Nan не равен числу и одновременно несравним - это ИМХО нормально. Число может быть неравно другому числу и одновременно больше другого числа. А другое может быть неравно и одновременно меньше. Это же не вызывает вопросов? Собственно, неравенство может получаться если число больше, меньше или несравнимо с другим.

Математику учить надо, чтобы понимать зачем существуют категории сравнений. Ничего сложного там нет, действительно только больше-меньше-равно-несравнимо, только для strong_ordering несравнимо не бывает и *читайте доку*

А статья буквально ничего не говорит полезного. Ни про типы сравнений, ни про то зачем оно нужно, ни про перегрузки операторов и = default. Ни че го

На практике они зачем нужны, в реальном программировании? Если у вас есть два числа int, то они никогда не будут несравнимы. А если у вас есть два double, то могут оказаться несравнимы, даже если вы примените strong_ordering. Что там в этом случае будет? Исключение кинется чтоли?

Всю математику в код не затащить по определению. Поэтому я склонен к поиску баланса между математической строгостью и практической целесообразностью.

А статья - это просто повод пообсуждать интересную тему:)

А если у вас есть два double, то могут оказаться несравнимы, даже если вы примените strong_ordering

как же это вы примените strong_ordering тогда? Для этого и нужно, такая ошибка обнаружится на компиляции.
А специальное сравнение std::three_way_compare вроде бы сделает специальную обработку даблов чтобы там был реальный strong_ordering

Вообще в вычислительных приложениях принято отличать signaling_nan от quiet_nan.

Лучшее враг хорошего

Что Страуструп сам то по этому поводу думает?

Я не понял, std::strong_ordering::less и < – это одно и то же или нет?

Если да, то зачем так по-разному? Если нет, то к чему это всё?

Чтобы писать одну единственную перегрузку вместо 3+ в обычном С++. Всё. То бишь синтаксический сахар. Все сравнения этим оператором напрямую в коде смысла не имеют и автор делает это только для того чтобы посмотреть что же там вернётся.

Зачем нужен этот новый оператор? Самый простой и обоснованный ответ это автоматическая генерация всех остальных операторов. Самое важное в нем то, что компилятор может предоставить дефолтную реализацию. Что это значит на практике?

struct X{
private: int data = {};
public: constexpr auto operator <=>(const &X, const &X) = default;)
}

Все, объекты класса Х теперь можно сравнивать друг с другом - не надо писать километры бойлер плэйт кода. Когда это не сработает? Тогда когда есть члены класса у которых нет дефолтного оператора <=> тогда придется немного повозиться и определить аж 2 оператора: == и вот этот новый спэйсшип, остальные будут сгенерированы сами, но это все равно сильно меньше чем дедовским способом и самое главное у вас не будет места для тупой опечатки, семантика всех операторов будет согласованной и корректной.

Есть ещё одно место зачем он нужен, например вызов дженерик сортировки для массива флотов/даблов это вообще-то УБ, внезапно да? Вот у этих самых сторонников дедовских способов щас уверен подгорело, сто лет так пишем и никаких УБ не видели. Есть даже целый ток от Шона (забыл фамилию), который из адоба, который детально объясняет почему так. Так вот что бы писать алгоритмы правильно и выставлять наружу требования к типам собственно и нужны эти новые типы одеринга и именно поэтому они разные, а не как тут товарищ выше предлагал все в один энум запихать. Алгоритм может быть перегружен для правильной сортировки даблов как раз за счёт разных типов одеринга, да и чего угодно с партиал одерингом, называется топологическая сортировка и работает совсем не так как ваши эти квик сорты, которые вообще говоря требуют вик одеринга.

Зачем на практике нужно различать Вик и Стронг я пока не нашел, а в чем разница спросите вы? Ну при Стронг одеринге и == ГАРАНТИРОВАНО можете использовать хоть правый хоть левый операнд в ЛЮБОЙ функции и получать один и тот же результат. Ну это только если человек который реализовывал спэйсшип возвращающий Стронг ордеринг понимал в чем разница и не допустил ошибок.

Я хз где это ограничение вообще можно применить, ну вот Инты у них Стронг ордеринг, а у структуры где есть много полей, и сравнение ведётся только по части из них по определению Вик ордеринг, ну и чё?

Этот момент конечно очень слабо освящён и допустить ошибку очень просто если сами реализуете, а не компилятор дефолт генерит можно по ошибке/не знанию/не пониманию объявить тип возвращаемого значения оператора спэйсшип как Стронг ордеринг, но это почти навярнека ошибка иначе вы бы использовали дефолтную реализацию.

Я хз где это ограничение вообще можно применить, ну вот Инты у них Стронг ордеринг, а у структуры где есть много полей, и сравнение ведётся только по части из них по определению Вик ордеринг, ну и чё?

Ну я могу себе представить, где это можно применить. Например, если есть некий контейнер для хранения объектов, то то, что два каких-то объекта равны в соответствии со strong_ordering гарантирует, что вместо хранения этих двух (или больше) объектов можно хранить только один из них плюс их количество (счетчик дубликатов). А в случае равенства по weak_ordering - нет, нужно хранить все по отдельности.

В целом я с вами согласен, но на практике такой тип скорее всего будет и так маленький по размеру, поэтому такая оптимизация будет скорее всего бессмысленной, ибо вам придется хранить такие объекты в куче + виртуальность либо в варианте либо иметь 2 контейнера. В общем на практике найти применение этому сложно.

При тех условиях, при которых сравнение даблов не имеет достаточной для сортировки упорядоченности (т.е. при учёте специальных значений) их сортировка вообще невозможна, так как доступ к значению signaling_nan может приводить к остановке работы программы.

Вы забыли про инфы и не сигнальные наны. Их как будете сортировать? Они ж как нулы в эскуэле, не сравнимы сами с собой, а наны ещё и ни с чем другим. Вот когда их исключите из массива вот тогда у вас вик ордеринг получится. Хотел написать сначало, что стронг, но кажется два нуля не позволяют это сказать, но я не уверен. Правильная сортировка даблов это сложно.

Да, я тоже сначала хотел написать про стронг, а потом подумал про два нуля.

Они ж как нулы в эскуэле, не сравнимы сами с собой, а наны ещё и ни с чем другим.

Это вы как раз с SQL путаете.

Hidden text

program nans

  use IEEE_Arithmetic

  real :: nanp, infp, infm, zerop, zerom
  logical, allocatable, dimension (:) :: testgt, testeq, testlt, testsf

  nanp = IEEE_Value (nanp, IEEE_QUIET_NAN)
  infp = IEEE_Value (infp, IEEE_POSITIVE_INF)
  infm = IEEE_Value (infm, IEEE_NEGATIVE_INF)
  zerop = IEEE_Value (zerop, IEEE_POSITIVE_ZERO)
  zerom = IEEE_Value (zerom, IEEE_NEGATIVE_ZERO)

  testgt = [nanp>0,infp>0,infm>0,zerop>0,zerom>0]
  testeq = [nanp==0,infp==0,infm==0,zerop==0,zerom==0]
  testlt = [nanp<0,infp<0,infm<0,zerop<0,zerom<0]
  testsf = [nanp==nanp, infp==infp, infm==infm, zerop==zerop, zerom==zerom]

  print *, testgt
  print *, testeq
  print *, testlt
  print *, testsf

end program nans
 F T F F F
 F F F T T
 F F T F F
 F T T T T

Но я как раз про это и написал. Если мы учитываем молчаливые nan, тогда должны учитывать и сигнальные nan, а с последними невозможны вообще никакие действия.

When a signaling NaN is used as an argument to an arithmetic expression, the appropriate floating-point exception may be raised

Ни IEEE-754, ни C++ не гарантируют никакого конкретного поведения с сигнальным nan. В частности, не гарантируют, что прерывание от сигнального nan можно будет поймать программно. И тем более точно определить точку его возникновения, если одновременно обрабатывается длинный конвейер. И тем более вернуться в неё для возобновления вычислений.

Ну может и так только код все равно продолжит работать, а не

а с последними невозможны вообще никакие действия.

Т.е. действия вполне возможны и результат тоже ясный - нан, но вот флаг эксепции может стоять, а может и нет - ну ладно.

| Есть даже целый ток от Шона (забыл фамилию), который из адоба, который детально объясняет почему так.

Вероятно речь про Sean Parent, правда не знаю о каком конкретно выступлении идёт речь

В C++20 вместе с этим оператором завезли ещё такой breaking change: при упорядочивании двух std::pair с помощью operator< будет использоваться operator<=> на компонентах этой пары. Это может выстрелить в классах, у которых есть операторы неявного приведения, потому что operator<=> предпочтёт другой operator<=>, даже если в классе уже есть написаный руками operator<, а для сравнения нужно приведение типов.

Про дефолтные ключи - прям смешно. Всем известно, что у gcc дефолт -O0, т.е. вообще без оптимизаций. Использовать можно или если сильно торопитесь бинарь получить, или как последняя надежда в gdb понять, что происходит.

Укажите хотя бы -Og, тогда и поговорим про ассемблер.

Я думал, что это чисто чтобы нормально сравнивать объекты, а не примитивные типы. Типа для нормальной работы коллекций, сортировки и тп. То есть я его понимаю как аналог интерфейса для сравнения объектов из шарпов

Да, это и есть прямой аналог IComaparable, только вместо трёх вариантов (<, =, >) он может вернуть ещё что-то. Тут, мне кажется, комитет перестарался впихнуть невпихуемое и вообще нарушил YAGNI, но в плюсах всё так и происходит...

разработчики стандарта затолкали его в язык, а необходимые ему для работы значения вынесли наружу.

Ровно также они поступили с C++20 Coroutines. Как бы поддержка в языке есть, а из-коробки не работает, потому что в либси++ не поддерживается.

Вообще, было бы круто, чтобы stdio было такое всё async, но для этого надо поддержку io_uring или в Windows OVERLAPPED использовать. Но было бы вообще здорово, чтобы я писал на C++ не задумываясь, что там, как на std::thread, и где-то пул io-воркеров, где-то нативные вызовы в ОС, даже boost::asio так не умеет!

Sign up to leave a comment.

Articles