При объявлении оператора трёхстороннего сравнения со значением default у класса с полями разных типов что определяет категорию сравнения всего класса?
Категория сравнения определяется результатами сравнения всех базовых подобъектов и нестатических полей в порядке их объявления. При автоматически выведенном возвращаемом типе выбирается общая, то есть не более сильная, категория: наличие поля с частичным порядком делает сравнение всего класса частично упорядоченным.
До C++20 классу часто требовалось вручную реализовывать несколько операторов сравнения. Это приводило к дублированию лексикографической логики и риску расхождения между реализациями операторов равенства и порядка.
Оператор трёхстороннего сравнения C++20 позволяет описать эту логику декларативно. Значение default поручает компилятору сравнить подобъекты последовательно и вывести свойства результата.
Рассматривать автоматически созданное сравнение как безусловный полный порядок опасно. Например, double поддерживает частичный порядок: сравнение с NaN не является ни меньше, ни равно, ни больше.
Если хотя бы один подобъект не поддерживает требуемое сравнение или его результат нельзя согласовать с возвращаемым типом, defaulted-оператор может оказаться удалённым. Поэтому категория сравнения влияет не только на тип результата, но и на доступные гарантии алгоритмов.
При defaulted-операторе компилятор сравнивает сначала базовые подобъекты, затем нестатические поля в порядке объявления. Сравнение прекращается на первом результате, отличном от равенства, поэтому семантика является лексикографической.
Если возвращаемый тип указан как auto, компилятор выводит общую категорию результатов сравнений подобъектов:
std::strong_ordering;std::weak_ordering;std::partial_ordering.Частичный порядок допускает значение unordered. Для него нельзя корректно предполагать, что из отсутствия меньшего автоматически следует большее или равенство. Типичный источник такого результата — плавающие значения, содержащие NaN.
Если оператор объявлен с конкретным возвращаемым типом, результаты сравнений подобъектов должны быть совместимы с ним. Нельзя честно объявить сильный порядок для класса, содержащего поле, сравнение которого допускает неупорядоченность.
Результат оператора можно использовать в реляционных сравнениях через правила переписывания выражений. Однако это не превращает частичный порядок в полный и не устраняет семантические свойства отдельных полей.
Минимальный пример:
Поле id сравнивается с сильным порядком, а value — с частичным. Общая категория становится std::partial_ordering, поскольку результат класса должен учитывать возможность неупорядоченного сравнения double.
В структуре конфигурации есть числовой идентификатор, строковое имя и измеряемый параметр типа double. Команда хочет использовать объект как ключ в упорядоченном контейнере и объявляет сравнение со значением default.
Ручная реализация сравнения позволила бы искусственно игнорировать NaN или нормализовать его, но она увеличивает объём кода и требует отдельно поддерживать равенство и порядок. Простое defaulted-сравнение прозрачно и безопасно отражает свойства данных, однако не даёт гарантии полного порядка.
Выбранное решение зависит от инвариантов домена. Если NaN недопустим, его следует отклонять при создании объекта или нормализовать заранее; тогда можно рассмотреть сильный или слабый порядок для подходящего представления. Если NaN является допустимым состоянием, нужно сохранять частичный порядок и не использовать результат как обычный полный порядок без дополнительной политики.
Такой подход предотвращает скрытую ошибку: контейнер или алгоритм не получает ложное обещание, что любые два объекта можно строго упорядочить.
Вопрос: Почему порядок объявления полей влияет на результат defaulted-сравнения?
Ответ: Сравнение выполняется лексикографически. Сначала сравнивается первый подобъект, затем следующий только при равенстве предыдущего. Поэтому изменение порядка полей может изменить результат сравнения, даже если набор полей остался тем же. Это также означает, что порядок должен соответствовать смысловому приоритету ключевых свойств объекта.
Вопрос: Можно ли объявить для класса сильный порядок, если одно из его полей имеет частичный порядок?
Ответ: В общем случае нет. Сильный порядок требует транзитивной и полной упорядоченности результатов, а частичное сравнение допускает состояние unordered. Defaulted-оператор с сильным возвращаемым типом будет некорректен, если результат сравнения поля нельзя привести к требуемой категории. Нужно изменить представление данных, ввести проверку инвариантов или явно реализовать доменную политику сравнения.
Вопрос: Что произойдёт, если поле вообще не поддерживает оператор трёхстороннего сравнения?
Ответ: Автоматически созданный оператор не сможет корректно сравнить все подобъекты и будет определён как удалённый либо вызовет ошибку на этапе использования, в зависимости от конкретного контекста и доступных операторов. Само наличие default не заставляет компилятор придумывать сравнение для несравнимого типа. Для такого поля требуется предоставить подходящий оператор, исключить его из сравнения собственной реализацией или явно определить другую семантику класса.